Skip to content
Open
Show file tree
Hide file tree
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
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ name = "fxhash"
path = "bench.rs"

[dependencies]
byteorder = "1.0.0"

[dev-dependencies]
seahash = "3.0.5"
fnv = "1.0.5"
fnv = "1.0.5"
152 changes: 111 additions & 41 deletions lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@
use std::collections::{HashMap, HashSet};
use std::default::Default;
use std::hash::{BuildHasherDefault, Hash, Hasher};
use std::ops::BitXor;

extern crate byteorder;
use byteorder::{ByteOrder, NativeEndian};

/// A builder for default Fx hashers.
pub type FxBuildHasher = BuildHasherDefault<FxHasher>;
Expand Down Expand Up @@ -61,73 +57,137 @@ trait HashWord {
fn hash_word(&mut self, Self);
}

macro_rules! impl_hash_word {
($($ty:ty = $key:ident),* $(,)*) => (
$(
impl HashWord for $ty {
#[inline]
fn hash_word(&mut self, word: Self) {
*self = self.rotate_left(ROTATE).bitxor(word).wrapping_mul($key);
}
}
)*
)
#[inline]
const fn hash_word_32(mut hash: u32, word: u32) -> u32 {
hash = hash.rotate_left(ROTATE);
hash = hash ^ word;
hash = hash.wrapping_mul(SEED32);
hash
}

impl_hash_word!(usize = SEED, u32 = SEED32, u64 = SEED64);
impl HashWord for u32 {
#[inline]
fn hash_word(&mut self, word: Self) {
*self = hash_word_32(*self, word);
}
}

#[inline]
fn write32(mut hash: u32, mut bytes: &[u8]) -> u32 {
while bytes.len() >= 4 {
hash.hash_word(NativeEndian::read_u32(bytes));
bytes = &bytes[4..];
const fn write32(mut hash: u32, bytes: &[u8]) -> u32 {
let mut cursor = 0;

while bytes.len() - cursor >= 4 {
let word = u32::from_ne_bytes([
bytes[cursor],
bytes[cursor+1],
bytes[cursor+2],
bytes[cursor+3]
]);
hash = hash_word_32(hash, word);
cursor += 4;
}

if bytes.len() >= 2 {
hash.hash_word(u32::from(NativeEndian::read_u16(bytes)));
bytes = &bytes[2..];
if bytes.len() - cursor >= 2 {
let word = u16::from_ne_bytes([
bytes[cursor],
bytes[cursor+1]
]);
hash = hash_word_32(hash, word as u32);
cursor += 2;
}

if let Some(&byte) = bytes.first() {
hash.hash_word(u32::from(byte));
if bytes.len() - cursor >= 1 {
hash = hash_word_32(hash, bytes[cursor] as u32);
}

hash
}

#[inline]
fn write64(mut hash: u64, mut bytes: &[u8]) -> u64 {
while bytes.len() >= 8 {
hash.hash_word(NativeEndian::read_u64(bytes));
bytes = &bytes[8..];
}
const fn hash_word_64(mut hash: u64, word: u64) -> u64 {
hash = hash.rotate_left(ROTATE);
hash = hash ^ word;
hash = hash.wrapping_mul(SEED64);
hash
}

if bytes.len() >= 4 {
hash.hash_word(u64::from(NativeEndian::read_u32(bytes)));
bytes = &bytes[4..];
impl HashWord for u64 {
#[inline]
fn hash_word(&mut self, word: Self) {
*self = hash_word_64(*self, word);
}
}

if bytes.len() >= 2 {
hash.hash_word(u64::from(NativeEndian::read_u16(bytes)));
bytes = &bytes[2..];
#[inline]
const fn write64(mut hash: u64, bytes: &[u8]) -> u64 {
let mut cursor = 0;

while bytes.len() - cursor >= 8 {
let word = u64::from_ne_bytes([
bytes[cursor],
bytes[cursor+1],
bytes[cursor+2],
bytes[cursor+3],
bytes[cursor+4],
bytes[cursor+5],
bytes[cursor+6],
bytes[cursor+7],
]);
hash = hash_word_64(hash, word);
cursor += 8;
}

while bytes.len() - cursor >= 4 {
let word = u32::from_ne_bytes([
bytes[cursor],
bytes[cursor+1],
bytes[cursor+2],
bytes[cursor+3]
]);
hash = hash_word_64(hash, word as u64);
cursor += 4;
}

if bytes.len() - cursor >= 2 {
let word = u16::from_ne_bytes([
bytes[cursor],
bytes[cursor+1]
]);
hash = hash_word_64(hash, word as u64);
cursor += 2;
}

if bytes.len() - cursor >= 1 {
hash = hash_word_64(hash, bytes[cursor] as u64);
}

if let Some(&byte) = bytes.first() {
hash.hash_word(u64::from(byte));
}
hash
}

#[inline]
const fn hash_word(mut hash: usize, word: usize) -> usize {
hash = hash.rotate_left(ROTATE);
hash = hash ^ word;
hash = hash.wrapping_mul(SEED);
hash
}

impl HashWord for usize {
#[inline]
fn hash_word(&mut self, word: Self) {
*self = hash_word(*self, word);
}
}

#[inline]
#[cfg(target_pointer_width = "32")]
fn write(hash: usize, bytes: &[u8]) -> usize {
const fn write(hash: usize, bytes: &[u8]) -> usize {
write32(hash as u32, bytes) as usize
}

#[inline]
#[cfg(target_pointer_width = "64")]
fn write(hash: usize, bytes: &[u8]) -> usize {
const fn write(hash: usize, bytes: &[u8]) -> usize {
write64(hash as u64, bytes) as usize
}

Expand Down Expand Up @@ -337,3 +397,13 @@ pub fn hash<T: Hash + ?Sized>(v: &T) -> usize {
v.hash(&mut state);
state.finish() as usize
}

/// A const function for when you need a 32-bit hash of a byte array.
pub const fn hash32_bytes(v: &[u8]) -> u32 {
write32(0, v)
}

/// A const function for when you need a 64-bit hash of a byte array.
pub const fn hash64_bytes(v: &[u8]) -> u64 {
write64(0, v)
}