Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Properly support cross compiled builds
The default Hash implementations aren't always invariant between
architectures, so we're defining our own trait. Indexing operations are
also carried out in u32 instead of uint as well.

Closes #9
  • Loading branch information
sfackler committed Aug 9, 2014
1 parent 3ab5bd1 commit b2220d9
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 77 deletions.
72 changes: 34 additions & 38 deletions phf/src/lib.rs
Expand Up @@ -7,13 +7,15 @@
#![crate_type="rlib"]
#![crate_type="dylib"]
#![warn(missing_doc)]
#![feature(macro_rules)]

use std::fmt;
use std::hash::Hash;
use std::iter;
use std::slice;
use std::collections::Collection;

pub use shared::PhfHash;

#[path="../../shared/mod.rs"]
mod shared;

Expand Down Expand Up @@ -44,11 +46,9 @@ mod shared;
/// be accessed directly.
pub struct PhfMap<K, V> {
#[doc(hidden)]
pub k1: u64,
#[doc(hidden)]
pub k2: u64,
pub key: u64,
#[doc(hidden)]
pub disps: &'static [(uint, uint)],
pub disps: &'static [(u32, u32)],
#[doc(hidden)]
pub entries: &'static [(K, V)],
}
Expand All @@ -59,7 +59,7 @@ impl<K, V> Collection for PhfMap<K, V> {
}
}

impl<'a, K: Hash+Eq, V> Map<K, V> for PhfMap<K, V> {
impl<'a, K: PhfHash+Eq, V> Map<K, V> for PhfMap<K, V> {
fn find(&self, key: &K) -> Option<&V> {
self.get_entry(key, |k| key == k).map(|e| {
let &(_, ref v) = e;
Expand All @@ -83,13 +83,13 @@ impl<K: fmt::Show, V: fmt::Show> fmt::Show for PhfMap<K, V> {
}
}

impl<K: Hash+Eq, V> Index<K, V> for PhfMap<K, V> {
impl<K: PhfHash+Eq, V> Index<K, V> for PhfMap<K, V> {
fn index(&self, k: &K) -> &V {
self.find(k).expect("invalid key")
}
}

impl<K: Hash+Eq, V> PhfMap<K, V> {
impl<K: PhfHash+Eq, V> PhfMap<K, V> {
/// Returns a reference to the map's internal static instance of the given
/// key.
///
Expand All @@ -103,12 +103,11 @@ impl<K: Hash+Eq, V> PhfMap<K, V> {
}

impl<K, V> PhfMap<K, V> {
fn get_entry<T: Hash>(&self, key: &T, check: |&K| -> bool)
-> Option<&(K, V)> {
let (g, f1, f2) = shared::hash(key, self.k1, self.k2);
let (d1, d2) = self.disps[g % self.disps.len()];
let entry = &self.entries[shared::displace(f1, f2, d1, d2) %
self.entries.len()];
fn get_entry<T: PhfHash>(&self, key: &T, check: |&K| -> bool) -> Option<&(K, V)> {
let (g, f1, f2) = key.phf_hash(self.key);
let (d1, d2) = self.disps[(g % (self.disps.len() as u32)) as uint];
let entry = &self.entries[(shared::displace(f1, f2, d1, d2) % (self.entries.len() as u32))
as uint];
let &(ref s, _) = entry;
if check(s) {
Some(entry)
Expand All @@ -118,7 +117,7 @@ impl<K, V> PhfMap<K, V> {
}

/// Like `find`, but can operate on any type that is equivalent to a key.
pub fn find_equiv<T: Hash+Equiv<K>>(&self, key: &T) -> Option<&V> {
pub fn find_equiv<T: PhfHash+Equiv<K>>(&self, key: &T) -> Option<&V> {
self.get_entry(key, |k| key.equiv(k)).map(|e| {
let &(_, ref v) = e;
v
Expand All @@ -127,7 +126,7 @@ impl<K, V> PhfMap<K, V> {

/// Like `find_key`, but can operate on any type that is equivalent to a
/// key.
pub fn find_key_equiv<T: Hash+Equiv<K>>(&self, key: &T) -> Option<&K> {
pub fn find_key_equiv<T: PhfHash+Equiv<K>>(&self, key: &T) -> Option<&K> {
self.get_entry(key, |k| key.equiv(k)).map(|e| {
let &(ref k, _) = e;
k
Expand Down Expand Up @@ -279,7 +278,7 @@ impl<T> Collection for PhfSet<T> {
}
}

impl<'a, T: Hash+Eq> Set<T> for PhfSet<T> {
impl<'a, T: PhfHash+Eq> Set<T> for PhfSet<T> {
#[inline]
fn contains(&self, value: &T) -> bool {
self.map.contains_key(value)
Expand All @@ -296,7 +295,7 @@ impl<'a, T: Hash+Eq> Set<T> for PhfSet<T> {
}
}

impl<T: Hash+Eq> PhfSet<T> {
impl<T: PhfHash+Eq> PhfSet<T> {
/// Returns a reference to the set's internal static instance of the given
/// key.
///
Expand All @@ -311,14 +310,14 @@ impl<T> PhfSet<T> {
/// Like `contains`, but can operate on any type that is equivalent to a
/// value
#[inline]
pub fn contains_equiv<U: Hash+Equiv<T>>(&self, key: &U) -> bool {
pub fn contains_equiv<U: PhfHash+Equiv<T>>(&self, key: &U) -> bool {
self.map.find_equiv(key).is_some()
}

/// Like `find_key`, but can operate on any type that is equivalent to a
/// value
#[inline]
pub fn find_key_equiv<U: Hash+Equiv<T>>(&self, key: &U) -> Option<&T> {
pub fn find_key_equiv<U: PhfHash+Equiv<T>>(&self, key: &U) -> Option<&T> {
self.map.find_key_equiv(key)
}
}
Expand Down Expand Up @@ -386,11 +385,9 @@ impl<'a, T> ExactSize<&'a T> for PhfSetValues<'a, T> {}
/// never be accessed directly.
pub struct PhfOrderedMap<K, V> {
#[doc(hidden)]
pub k1: u64,
#[doc(hidden)]
pub k2: u64,
pub key: u64,
#[doc(hidden)]
pub disps: &'static [(uint, uint)],
pub disps: &'static [(u32, u32)],
#[doc(hidden)]
pub idxs: &'static [uint],
#[doc(hidden)]
Expand Down Expand Up @@ -418,7 +415,7 @@ impl<K, V> Collection for PhfOrderedMap<K, V> {
}
}

impl<K: Hash+Eq, V> Map<K, V> for PhfOrderedMap<K, V> {
impl<K: PhfHash+Eq, V> Map<K, V> for PhfOrderedMap<K, V> {
fn find(&self, key: &K) -> Option<&V> {
self.find_entry(key, |k| k == key).map(|e| {
let &(_, ref v) = e;
Expand All @@ -427,13 +424,13 @@ impl<K: Hash+Eq, V> Map<K, V> for PhfOrderedMap<K, V> {
}
}

impl<K: Hash+Eq, V> Index<K, V> for PhfOrderedMap<K, V> {
impl<K: PhfHash+Eq, V> Index<K, V> for PhfOrderedMap<K, V> {
fn index(&self, k: &K) -> &V {
self.find(k).expect("invalid key")
}
}

impl<K: Hash+Eq, V> PhfOrderedMap<K, V> {
impl<K: PhfHash+Eq, V> PhfOrderedMap<K, V> {
/// Returns a reference to the map's internal static instance of the given
/// key.
///
Expand All @@ -447,11 +444,10 @@ impl<K: Hash+Eq, V> PhfOrderedMap<K, V> {
}

impl<K, V> PhfOrderedMap<K, V> {
fn find_entry<T: Hash>(&self, key: &T, check: |&K| -> bool)
-> Option<&(K, V)> {
let (g, f1, f2) = shared::hash(key, self.k1, self.k2);
let (d1, d2) = self.disps[g % self.disps.len()];
let idx = self.idxs[shared::displace(f1, f2, d1, d2) % self.idxs.len()];
fn find_entry<T: PhfHash>(&self, key: &T, check: |&K| -> bool) -> Option<&(K, V)> {
let (g, f1, f2) = key.phf_hash(self.key);
let (d1, d2) = self.disps[(g % (self.disps.len() as u32)) as uint];
let idx = self.idxs[(shared::displace(f1, f2, d1, d2) % (self.idxs.len() as u32)) as uint];
let entry = &self.entries[idx];
let &(ref s, _) = entry;

Expand All @@ -463,7 +459,7 @@ impl<K, V> PhfOrderedMap<K, V> {
}

/// Like `find`, but can operate on any type that is equivalent to a key.
pub fn find_equiv<T: Hash+Equiv<K>>(&self, key: &T) -> Option<&V> {
pub fn find_equiv<T: PhfHash+Equiv<K>>(&self, key: &T) -> Option<&V> {
self.find_entry(key, |k| key.equiv(k)).map(|e| {
let &(_, ref v) = e;
v
Expand All @@ -472,7 +468,7 @@ impl<K, V> PhfOrderedMap<K, V> {

/// Like `find_key`, but can operate on any type that is equivalent to a
/// key.
pub fn find_key_equiv<T: Hash+Equiv<K>>(&self, key: &T) -> Option<&K> {
pub fn find_key_equiv<T: PhfHash+Equiv<K>>(&self, key: &T) -> Option<&K> {
self.find_entry(key, |k| key.equiv(k)).map(|e| {
let &(ref k, _) = e;
k
Expand Down Expand Up @@ -659,7 +655,7 @@ impl<T> Collection for PhfOrderedSet<T> {
}
}

impl<T: Hash+Eq> Set<T> for PhfOrderedSet<T> {
impl<T: PhfHash+Eq> Set<T> for PhfOrderedSet<T> {
#[inline]
fn contains(&self, value: &T) -> bool {
self.map.contains_key(value)
Expand All @@ -676,7 +672,7 @@ impl<T: Hash+Eq> Set<T> for PhfOrderedSet<T> {
}
}

impl<T: Hash+Eq> PhfOrderedSet<T> {
impl<T: PhfHash+Eq> PhfOrderedSet<T> {
/// Returns a reference to the set's internal static instance of the given
/// key.
///
Expand All @@ -691,14 +687,14 @@ impl<T> PhfOrderedSet<T> {
/// Like `contains`, but can operate on any type that is equivalent to a
/// value
#[inline]
pub fn contains_equiv<U: Hash+Equiv<T>>(&self, key: &U) -> bool {
pub fn contains_equiv<U: PhfHash+Equiv<T>>(&self, key: &U) -> bool {
self.map.find_equiv(key).is_some()
}

/// Like `find_key`, but can operate on any type that is equivalent to a
/// value
#[inline]
pub fn find_key_equiv<U: Hash+Equiv<T>>(&self, key: &U) -> Option<&T> {
pub fn find_key_equiv<U: PhfHash+Equiv<T>>(&self, key: &U) -> Option<&T> {
self.map.find_key_equiv(key)
}

Expand Down
72 changes: 42 additions & 30 deletions phf_mac/src/lib.rs
Expand Up @@ -4,7 +4,7 @@
#![crate_name="phf_mac"]
#![crate_type="dylib"]
#![doc(html_root_url="http://sfackler.github.io/rust-phf/doc")]
#![feature(plugin_registrar, quote, default_type_params)]
#![feature(plugin_registrar, quote, default_type_params, macro_rules)]

extern crate rand;
extern crate syntax;
Expand All @@ -13,10 +13,10 @@ extern crate rustc;

use std::collections::HashMap;
use std::gc::{Gc, GC};
use std::hash;
use std::hash::Hash;
use std::os;
use std::rc::Rc;
use std::hash;
use std::hash::Hash;
use syntax::ast;
use syntax::ast::{TokenTree, LitStr, LitBinary, LitByte, LitChar, Expr, ExprVec, ExprLit};
use syntax::codemap::Span;
Expand All @@ -31,6 +31,8 @@ use syntax::print::pprust;
use rand::{Rng, SeedableRng, XorShiftRng};
use rustc::plugin::Registry;

use shared::PhfHash;

#[path="../../shared/mod.rs"]
mod shared;

Expand Down Expand Up @@ -82,16 +84,34 @@ impl<S: hash::Writer> Hash<S> for Key {
}
}

impl PhfHash for Key {
fn phf_hash(&self, key: u64) -> (u32, u32, u32) {
match *self {
KeyStr(ref s) => s.get().phf_hash(key),
KeyBinary(ref b) => b.as_slice().phf_hash(key),
KeyChar(c) => c.phf_hash(key),
KeyU8(b) => b.phf_hash(key),
KeyI8(b) => b.phf_hash(key),
KeyU16(b) => b.phf_hash(key),
KeyI16(b) => b.phf_hash(key),
KeyU32(b) => b.phf_hash(key),
KeyI32(b) => b.phf_hash(key),
KeyU64(b) => b.phf_hash(key),
KeyI64(b) => b.phf_hash(key),
KeyBool(b) => b.phf_hash(key),
}
}
}

struct Entry {
key_contents: Key,
key: Gc<Expr>,
value: Gc<Expr>
}

struct HashState {
k1: u64,
k2: u64,
disps: Vec<(uint, uint)>,
key: u64,
disps: Vec<(u32, u32)>,
map: Vec<uint>,
}

Expand Down Expand Up @@ -334,16 +354,15 @@ fn try_generate_hash(entries: &[Entry], rng: &mut XorShiftRng)
}

struct Hashes {
g: uint,
f1: uint,
f2: uint,
g: u32,
f1: u32,
f2: u32,
}

let k1 = rng.gen();
let k2 = rng.gen();
let key = rng.gen();

let hashes: Vec<Hashes> = entries.iter().map(|entry| {
let (g, f1, f2) = shared::hash(&entry.key_contents, k1, k2);
let (g, f1, f2) = entry.key_contents.phf_hash(key);
Hashes {
g: g,
f1: f1,
Expand All @@ -356,25 +375,23 @@ fn try_generate_hash(entries: &[Entry], rng: &mut XorShiftRng)
|i| Bucket { idx: i, keys: Vec::new() });

for (i, hash) in hashes.iter().enumerate() {
buckets.get_mut(hash.g % buckets_len).keys.push(i);
buckets.get_mut((hash.g % (buckets_len as u32)) as uint).keys.push(i);
}

// Sort descending
buckets.sort_by(|a, b| b.keys.len().cmp(&a.keys.len()));

let table_len = entries.len();
let mut map = Vec::from_elem(table_len, None);
let mut disps = Vec::from_elem(buckets_len, (0u, 0u));
let mut disps = Vec::from_elem(buckets_len, (0u32, 0u32));
let mut try_map = HashMap::new();
'buckets: for bucket in buckets.iter() {
for d1 in range(0, table_len) {
'disps: for d2 in range(0, table_len) {
for d1 in range(0, table_len as u32) {
'disps: for d2 in range(0, table_len as u32) {
try_map.clear();
for &key in bucket.keys.iter() {
let idx = shared::displace(hashes[key].f1,
hashes[key].f2,
d1,
d2) % table_len;
let idx = (shared::displace(hashes[key].f1, hashes[key].f2, d1, d2)
% (table_len as u32)) as uint;
if map[idx].is_some() || try_map.find(&idx).is_some() {
continue 'disps;
}
Expand All @@ -395,8 +412,7 @@ fn try_generate_hash(entries: &[Entry], rng: &mut XorShiftRng)
}

Some(HashState {
k1: k1,
k2: k2,
key: key,
disps: disps,
map: map.move_iter().map(|i| i.unwrap()).collect(),
})
Expand All @@ -415,11 +431,9 @@ fn create_map(cx: &mut ExtCtxt, sp: Span, entries: Vec<Entry>, state: HashState)
}).collect();
let entries = create_slice_expr(entries, sp);

let k1 = state.k1;
let k2 = state.k2;
let key = state.key;
MacExpr::new(quote_expr!(cx, ::phf::PhfMap {
k1: $k1,
k2: $k2,
key: $key,
disps: &$disps,
entries: &$entries,
}))
Expand All @@ -446,11 +460,9 @@ fn create_ordered_map(cx: &mut ExtCtxt, sp: Span, entries: Vec<Entry>,
}).collect();
let entries = create_slice_expr(entries, sp);

let k1 = state.k1;
let k2 = state.k2;
let key = state.key;
MacExpr::new(quote_expr!(cx, ::phf::PhfOrderedMap {
k1: $k1,
k2: $k2,
key: $key,
disps: &$disps,
idxs: &$idxs,
entries: &$entries,
Expand Down

0 comments on commit b2220d9

Please sign in to comment.