diff --git a/phf/src/lib.rs b/phf/src/lib.rs index 173377ec..ae1e8c54 100644 --- a/phf/src/lib.rs +++ b/phf/src/lib.rs @@ -6,11 +6,33 @@ #![warn(missing_doc)] use std::fmt; -use std::hash::Hasher; +use std::hash::{Hash, Hasher}; use std::hash::sip::SipHasher; use std::slice; use std::collections::Collection; +static LOG_MAX_SIZE: uint = 21; + +#[doc(hidden)] +pub static MAX_SIZE: uint = 1 << LOG_MAX_SIZE; + +#[doc(hidden)] +#[inline] +pub fn hash(s: &T, k1: u64, k2: u64) -> (uint, uint, uint) { + let hash = SipHasher::new_with_keys(k1, k2).hash(s); + let mask = (MAX_SIZE - 1) as u64; + + ((hash & mask) as uint, + ((hash >> LOG_MAX_SIZE) & mask) as uint, + ((hash >> (2 * LOG_MAX_SIZE)) & mask) as uint) +} + +#[doc(hidden)] +#[inline] +pub fn displace(f1: uint, f2: uint, d1: uint, d2: uint) -> uint { + d2 + f1 * d1 + f2 +} + /// An immutable map constructed at compile time. /// /// `PhfMap`s may be created with the `phf_map` macro: @@ -23,7 +45,7 @@ use std::collections::Collection; /// /// use phf::PhfMap; /// -/// static MY_MAP: PhfMap = phf_map! { +/// static MY_MAP: PhfMap<&'static str, int> = phf_map! { /// "hello" => 10, /// "world" => 11, /// }; @@ -36,7 +58,7 @@ use std::collections::Collection; /// The fields of this struct are public so that they may be initialized by the /// `phf_map` macro. They are subject to change at any time and should never /// be accessed directly. -pub struct PhfMap { +pub struct PhfMap { #[doc(hidden)] pub k1: u64, #[doc(hidden)] @@ -44,48 +66,29 @@ pub struct PhfMap { #[doc(hidden)] pub disps: &'static [(uint, uint)], #[doc(hidden)] - pub entries: &'static [(&'static str, T)], -} - -static LOG_MAX_SIZE: uint = 21; - -#[doc(hidden)] -pub static MAX_SIZE: uint = 1 << LOG_MAX_SIZE; - -#[doc(hidden)] -#[inline] -pub fn hash(s: &str, k1: u64, k2: u64) -> (uint, uint, uint) { - let hash = SipHasher::new_with_keys(k1, k2).hash(&s); - let mask = (MAX_SIZE - 1) as u64; - - ((hash & mask) as uint, - ((hash >> LOG_MAX_SIZE) & mask) as uint, - ((hash >> (2 * LOG_MAX_SIZE)) & mask) as uint) + pub entries: &'static [(K, V)], } -#[doc(hidden)] -#[inline] -pub fn displace(f1: uint, f2: uint, d1: uint, d2: uint) -> uint { - d2 + f1 * d1 + f2 -} - -impl Collection for PhfMap { +impl Collection for PhfMap { fn len(&self) -> uint { self.entries.len() } } -impl<'a, T> Map<&'a str, T> for PhfMap { - fn find<'a>(&'a self, key: & &str) -> Option<&'a T> { - self.find_entry(key).map(|&(_, ref v)| v) +impl<'a, K: Hash+Eq, V> Map for PhfMap { + fn find<'a>(&'a self, key: &K) -> Option<&'a V> { + self.get_entry(key, |k| key == k).map(|e| { + let &(_, ref v) = e; + v + }) } } -impl fmt::Show for PhfMap { +impl fmt::Show for PhfMap { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { try!(write!(fmt, "{{")); let mut first = true; - for (k, v) in self.entries() { + for &(ref k, ref v) in self.entries() { if !first { try!(write!(fmt, ", ")); } @@ -96,13 +99,14 @@ impl fmt::Show for PhfMap { } } -impl PhfMap { - fn find_entry(&self, key: & &str) -> Option<&'static (&'static str, T)> { - let (g, f1, f2) = hash(*key, self.k1, self.k2); +impl PhfMap { + fn get_entry<'a, T: Hash>(&'a self, key: &T, check: |&K| -> bool) + -> Option<&'a (K, V)> { + let (g, f1, f2) = hash(key, self.k1, self.k2); let (d1, d2) = self.disps[g % self.disps.len()]; - let entry @ &(s, _) = &self.entries[displace(f1, f2, d1, d2) % - self.entries.len()]; - if s == *key { + let entry @ &(ref s, _) = &self.entries[displace(f1, f2, d1, d2) % + self.entries.len()]; + if check(s) { Some(entry) } else { None @@ -113,40 +117,64 @@ impl PhfMap { /// key. /// /// This can be useful for interning schemes. - pub fn find_key(&self, key: & &str) -> Option<&'static str> { - self.find_entry(key).map(|&(s, _)| s) + pub fn find_key<'a>(&'a self, key: &K) -> Option<&'a K> { + self.get_entry(key, |k| key == k).map(|e| { + let &(ref k, _) = e; + k + }) + } + + /// Like `find`, but can operate on any type that is equivalent to a key. + pub fn find_equiv<'a, T: Hash+Equiv>(&'a self, key: &T) + -> Option<&'a V> { + self.get_entry(key, |k| key.equiv(k)).map(|e| { + let &(_, ref v) = e; + v + }) + } + + /// Like `find_key`, but can operate on any type that is equivalent to a + /// key. + pub fn find_key_equiv<'a, T: Hash+Equiv>(&'a self, key: &T) + -> Option<&'a K> { + self.get_entry(key, |k| key.equiv(k)).map(|e| { + let &(ref k, _) = e; + k + }) } +} +impl PhfMap { /// Returns an iterator over the key/value pairs in the map. /// /// Entries are retuned in an arbitrary but fixed order. - pub fn entries<'a>(&'a self) -> PhfMapEntries<'a, T> { + pub fn entries<'a>(&'a self) -> PhfMapEntries<'a, K, V> { PhfMapEntries { iter: self.entries.iter() } } /// Returns an iterator over the keys in the map. /// /// Keys are returned in an arbitrary but fixed order. - pub fn keys<'a>(&'a self) -> PhfMapKeys<'a, T> { + pub fn keys<'a>(&'a self) -> PhfMapKeys<'a, K, V> { PhfMapKeys { iter: self.entries() } } /// Returns an iterator over the values in the map. /// /// Values are returned in an arbitrary but fixed order. - pub fn values<'a>(&'a self) -> PhfMapValues<'a, T> { + pub fn values<'a>(&'a self) -> PhfMapValues<'a, K, V> { PhfMapValues { iter: self.entries() } } } /// An iterator over the key/value pairs in a `PhfMap`. -pub struct PhfMapEntries<'a, T> { - iter: slice::Items<'a, (&'static str, T)>, +pub struct PhfMapEntries<'a, K, V> { + iter: slice::Items<'a, (K, V)>, } -impl<'a, T> Iterator<(&'static str, &'a T)> for PhfMapEntries<'a, T> { - fn next(&mut self) -> Option<(&'static str, &'a T)> { - self.iter.next().map(|&(key, ref value)| (key, value)) +impl<'a, K, V> Iterator<&'a (K, V)> for PhfMapEntries<'a, K, V> { + fn next(&mut self) -> Option<&'a (K, V)> { + self.iter.next() } fn size_hint(&self) -> (uint, Option) { @@ -155,13 +183,13 @@ impl<'a, T> Iterator<(&'static str, &'a T)> for PhfMapEntries<'a, T> { } /// An iterator over the keys in a `PhfMap`. -pub struct PhfMapKeys<'a, T> { - iter: PhfMapEntries<'a, T>, +pub struct PhfMapKeys<'a, K, V> { + iter: PhfMapEntries<'a, K, V>, } -impl<'a, T> Iterator<&'static str> for PhfMapKeys<'a, T> { - fn next(&mut self) -> Option<&'static str> { - self.iter.next().map(|(key, _)| key) +impl<'a, K, V> Iterator<&'a K> for PhfMapKeys<'a, K, V> { + fn next(&mut self) -> Option<&'a K> { + self.iter.next().map(|&(ref key, _)| key) } fn size_hint(&self) -> (uint, Option) { @@ -170,13 +198,13 @@ impl<'a, T> Iterator<&'static str> for PhfMapKeys<'a, T> { } /// An iterator over the values in a `PhfMap`. -pub struct PhfMapValues<'a, T> { - iter: PhfMapEntries<'a, T>, +pub struct PhfMapValues<'a, K, V> { + iter: PhfMapEntries<'a, K, V>, } -impl<'a, T> Iterator<&'a T> for PhfMapValues<'a, T> { - fn next(&mut self) -> Option<&'a T> { - self.iter.next().map(|(_, value)| value) +impl<'a, K, V> Iterator<&'a V> for PhfMapValues<'a, K, V> { + fn next(&mut self) -> Option<&'a V> { + self.iter.next().map(|&(_, ref value)| value) } fn size_hint(&self) -> (uint, Option) { @@ -196,7 +224,7 @@ impl<'a, T> Iterator<&'a T> for PhfMapValues<'a, T> { /// /// use phf::PhfSet; /// -/// static MY_SET: PhfSet = phf_set! { +/// static MY_SET: PhfSet<&'static str> = phf_set! { /// "hello", /// "world", /// }; @@ -209,12 +237,12 @@ impl<'a, T> Iterator<&'a T> for PhfMapValues<'a, T> { /// The fields of this struct are public so that they may be initialized by the /// `phf_set` macro. They are subject to change at any time and should never be /// accessed directly. -pub struct PhfSet { +pub struct PhfSet { #[doc(hidden)] - pub map: PhfMap<()> + pub map: PhfMap } -impl fmt::Show for PhfSet { +impl fmt::Show for PhfSet { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { try!(write!(fmt, "{{")); let mut first = true; @@ -229,57 +257,59 @@ impl fmt::Show for PhfSet { } } -impl Collection for PhfSet { +impl Collection for PhfSet { #[inline] fn len(&self) -> uint { self.map.len() } } -impl<'a> Set<&'a str> for PhfSet { +impl<'a, T: Hash+Eq> Set for PhfSet { #[inline] - fn contains(&self, value: & &'a str) -> bool { + fn contains(&self, value: &T) -> bool { self.map.contains_key(value) } #[inline] - fn is_disjoint(&self, other: &PhfSet) -> bool { - !self.iter().any(|value| other.contains(&value)) + fn is_disjoint(&self, other: &PhfSet) -> bool { + !self.iter().any(|value| other.contains(value)) } #[inline] - fn is_subset(&self, other: &PhfSet) -> bool { - self.iter().all(|value| other.contains(&value)) + fn is_subset(&self, other: &PhfSet) -> bool { + self.iter().all(|value| other.contains(value)) } } -impl PhfSet { +impl PhfSet { /// Returns a reference to the set's internal static instance of the given /// key. /// /// This can be useful for interning schemes. #[inline] - pub fn find_key(&self, key: & &str) -> Option<&'static str> { + pub fn find_key<'a>(&'a self, key: &T) -> Option<&'a T> { self.map.find_key(key) } +} +impl PhfSet { /// Returns an iterator over the values in the set. /// /// Values are returned in an arbitrary but fixed order. #[inline] - pub fn iter<'a>(&'a self) -> PhfSetValues<'a> { + pub fn iter<'a>(&'a self) -> PhfSetValues<'a, T> { PhfSetValues { iter: self.map.keys() } } } /// An iterator over the values in a `PhfSet`. -pub struct PhfSetValues<'a> { - iter: PhfMapKeys<'a, ()>, +pub struct PhfSetValues<'a, T> { + iter: PhfMapKeys<'a, T, ()>, } -impl<'a> Iterator<&'static str> for PhfSetValues<'a> { +impl<'a, T> Iterator<&'a T> for PhfSetValues<'a, T> { #[inline] - fn next(&mut self) -> Option<&'static str> { + fn next(&mut self) -> Option<&'a T> { self.iter.next() } @@ -359,7 +389,7 @@ impl<'a, T> Map<&'a str, T> for PhfOrderedMap { impl PhfOrderedMap { fn find_entry(&self, key: & &str) -> Option<&'static (&'static str, T)> { - let (g, f1, f2) = hash(*key, self.k1, self.k2); + let (g, f1, f2) = hash(key, self.k1, self.k2); let (d1, d2) = self.disps[g % self.disps.len()]; let idx = self.idxs[displace(f1, f2, d1, d2) % self.idxs.len()]; let entry @ &(s, _) = &self.entries[idx]; diff --git a/phf/src/test.rs b/phf/src/test.rs index aeba1688..2a948eed 100644 --- a/phf/src/test.rs +++ b/phf/src/test.rs @@ -9,18 +9,18 @@ mod map { use phf::PhfMap; #[allow(dead_code)] - static TRAILING_COMMA: PhfMap = phf_map!( + static TRAILING_COMMA: PhfMap<&'static str, int> = phf_map!( "foo" => 10, ); #[allow(dead_code)] - static NO_TRAILING_COMMA: PhfMap = phf_map!( + static NO_TRAILING_COMMA: PhfMap<&'static str, int> = phf_map!( "foo" => 10 ); #[test] fn test_two() { - static map: PhfMap = phf_map!( + static map: PhfMap<&'static str, int> = phf_map!( "foo" => 10, "bar" => 11, ); @@ -32,14 +32,11 @@ mod map { #[test] fn test_entries() { - static map: PhfMap = phf_map!( + static map: PhfMap<&'static str, int> = phf_map!( "foo" => 10, "bar" => 11, ); - let mut hash = HashMap::new(); - for (key, &value) in map.entries() { - hash.insert(key, value); - } + let hash = map.entries().map(|&e| e).collect::>(); assert!(Some(&10) == hash.find(&("foo"))); assert!(Some(&11) == hash.find(&("bar"))); assert_eq!(2, hash.len()); @@ -47,14 +44,11 @@ mod map { #[test] fn test_keys() { - static map: PhfMap = phf_map!( + static map: PhfMap<&'static str, int> = phf_map!( "foo" => 10, "bar" => 11, ); - let mut hash = HashSet::new(); - for key in map.keys() { - hash.insert(key); - } + let hash = map.keys().map(|&e| e).collect::>(); assert!(hash.contains(&("foo"))); assert!(hash.contains(&("bar"))); assert_eq!(2, hash.len()); @@ -62,14 +56,11 @@ mod map { #[test] fn test_values() { - static map: PhfMap = phf_map!( + static map: PhfMap<&'static str, int> = phf_map!( "foo" => 10, "bar" => 11, ); - let mut hash = HashSet::new(); - for &value in map.values() { - hash.insert(value); - } + let hash = map.values().map(|&e| e).collect::>(); assert!(hash.contains(&10)); assert!(hash.contains(&11)); assert_eq!(2, hash.len()); @@ -77,7 +68,7 @@ mod map { #[test] fn test_large() { - static map: PhfMap = phf_map!( + static map: PhfMap<&'static str, int> = phf_map!( "a" => 0, "b" => 1, "c" => 2, @@ -110,11 +101,19 @@ mod map { #[test] fn test_macro_key() { - static map: PhfMap = phf_map!( + static map: PhfMap<&'static str, int> = phf_map!( concat!("foo", "bar") => 1 ); assert!(Some(&1) == map.find(&("foobar"))); } + + #[test] + fn test_non_static_str_key() { + static map: PhfMap<&'static str, int> = phf_map!( + "a" => 0, + ); + assert_eq!(Some(&0), map.find_equiv(&"a".to_string().as_slice())); + } } mod set { @@ -122,18 +121,18 @@ mod set { use phf::PhfSet; #[allow(dead_code)] - static TRAILING_COMMA: PhfSet = phf_set! { + static TRAILING_COMMA: PhfSet<&'static str> = phf_set! { "foo", }; #[allow(dead_code)] - static NO_TRAILING_COMMA: PhfSet = phf_set! { + static NO_TRAILING_COMMA: PhfSet<&'static str> = phf_set! { "foo" }; #[test] fn test_two() { - static SET: PhfSet = phf_set! { + static SET: PhfSet<&'static str> = phf_set! { "hello", "world", }; @@ -145,11 +144,11 @@ mod set { #[test] fn test_iter() { - static SET: PhfSet = phf_set! { + static SET: PhfSet<&'static str> = phf_set! { "hello", "world", }; - let set = SET.iter().collect::>(); + let set = SET.iter().map(|e| *e).collect::>(); assert!(set.contains(&"hello")); assert!(set.contains(&"world")); assert_eq!(2, set.len()); diff --git a/phf_mac/src/lib.rs b/phf_mac/src/lib.rs index a3f0c6ed..d24ba32c 100644 --- a/phf_mac/src/lib.rs +++ b/phf_mac/src/lib.rs @@ -284,7 +284,7 @@ fn try_generate_hash(entries: &[Entry], rng: &mut XorShiftRng) let k2 = rng.gen(); let hashes: Vec = entries.iter().map(|entry| { - let (g, f1, f2) = phf::hash(entry.key_str.get(), k1, k2); + let (g, f1, f2) = phf::hash(&entry.key_str.get(), k1, k2); Hashes { g: g, f1: f1,