1// Copyright 2015 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.
1011//! Fast, non-cryptographic hash used by rustc and Firefox.
12//!
13//! # Example
14//!
15//! ```rust
16//! use rustc_hash::FxHashMap;
17//! let mut map: FxHashMap<u32, u32> = FxHashMap::default();
18//! map.insert(22, 44);
19//! ```
2021extern crate byteorder;
2223use std::collections::{HashMap, HashSet};
24use std::default::Default;
25use std::hash::{Hasher, BuildHasherDefault};
26use std::ops::BitXor;
27use std::mem::size_of;
2829use byteorder::{ByteOrder, NativeEndian};
3031/// Type alias for a hashmap using the `fx` hash algorithm.
32pub type FxHashMap<K, V> = HashMap<K, V, BuildHasherDefault<FxHasher>>;
3334/// Type alias for a hashmap using the `fx` hash algorithm.
35pub type FxHashSet<V> = HashSet<V, BuildHasherDefault<FxHasher>>;
3637/// A speedy hash algorithm for use within rustc. The hashmap in liballoc
38/// by default uses SipHash which isn't quite as speedy as we want. In the
39/// compiler we're not really worried about DOS attempts, so we use a fast
40/// non-cryptographic hash.
41///
42/// This is the same as the algorithm used by Firefox -- which is a homespun
43/// one not based on any widely-known algorithm -- though modified to produce
44/// 64-bit hash values instead of 32-bit hash values. It consistently
45/// out-performs an FNV-based hash within rustc itself -- the collision rate is
46/// similar or slightly worse than FNV, but the speed of the hash function
47/// itself is much higher because it works on up to 8 bytes at a time.
48pub struct FxHasher {
49 hash: usize
50}
5152#[cfg(target_pointer_width = "32")]
53const K: usize = 0x9e3779b9;
54#[cfg(target_pointer_width = "64")]
55const K: usize = 0x517cc1b727220a95;
5657impl Default for FxHasher {
58#[inline]
59fn default() -> FxHasher {
60 FxHasher { hash: 0 }
61 }
62}
6364impl FxHasher {
65#[inline]
66fn add_to_hash(&mut self, i: usize) {
67self.hash = self.hash.rotate_left(5).bitxor(i).wrapping_mul(K);
68 }
69}
7071impl Hasher for FxHasher {
72#[inline]
73fn write(&mut self, mut bytes: &[u8]) {
74#[cfg(target_pointer_width = "32")]
75let read_usize = |bytes| NativeEndian::read_u32(bytes);
76#[cfg(target_pointer_width = "64")]
77let read_usize = |bytes| NativeEndian::read_u64(bytes);
7879let mut hash = FxHasher { hash: self.hash };
80assert!(size_of::<usize>() <= 8);
81while bytes.len() >= size_of::<usize>() {
82 hash.add_to_hash(read_usize(bytes) as usize);
83 bytes = &bytes[size_of::<usize>()..];
84 }
85if (size_of::<usize>() > 4) && (bytes.len() >= 4) {
86 hash.add_to_hash(NativeEndian::read_u32(bytes) as usize);
87 bytes = &bytes[4..];
88 }
89if (size_of::<usize>() > 2) && bytes.len() >= 2 {
90 hash.add_to_hash(NativeEndian::read_u16(bytes) as usize);
91 bytes = &bytes[2..];
92 }
93if (size_of::<usize>() > 1) && bytes.len() >= 1 {
94 hash.add_to_hash(bytes[0] as usize);
95 }
96self.hash = hash.hash;
97 }
9899#[inline]
100fn write_u8(&mut self, i: u8) {
101self.add_to_hash(i as usize);
102 }
103104#[inline]
105fn write_u16(&mut self, i: u16) {
106self.add_to_hash(i as usize);
107 }
108109#[inline]
110fn write_u32(&mut self, i: u32) {
111self.add_to_hash(i as usize);
112 }
113114#[cfg(target_pointer_width = "32")]
115 #[inline]
116fn write_u64(&mut self, i: u64) {
117self.add_to_hash(i as usize);
118self.add_to_hash((i >> 32) as usize);
119 }
120121#[cfg(target_pointer_width = "64")]
122 #[inline]
123fn write_u64(&mut self, i: u64) {
124self.add_to_hash(i as usize);
125 }
126127#[inline]
128fn write_usize(&mut self, i: usize) {
129self.add_to_hash(i);
130 }
131132#[inline]
133fn finish(&self) -> u64 {
134self.hash as u64
135 }
136}