Skip to content

Commit

Permalink
Parameterize the key type of PhfMap and Set
Browse files Browse the repository at this point in the history
We don't actually support anything beyond &'static str right now, but
this will allow other keys in the future.
  • Loading branch information
sfackler committed Jul 10, 2014
1 parent a0ab8d7 commit cb4ed93
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 103 deletions.
184 changes: 107 additions & 77 deletions phf/src/lib.rs
Expand Up @@ -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<T: 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:
Expand All @@ -23,7 +45,7 @@ use std::collections::Collection;
///
/// use phf::PhfMap;
///
/// static MY_MAP: PhfMap<int> = phf_map! {
/// static MY_MAP: PhfMap<&'static str, int> = phf_map! {
/// "hello" => 10,
/// "world" => 11,
/// };
Expand All @@ -36,56 +58,37 @@ 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<T> {
pub struct PhfMap<K, V> {
#[doc(hidden)]
pub k1: u64,
#[doc(hidden)]
pub k2: u64,
#[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<T> Collection for PhfMap<T> {
impl<K, V> Collection for PhfMap<K, V> {
fn len(&self) -> uint {
self.entries.len()
}
}

impl<'a, T> Map<&'a str, T> for PhfMap<T> {
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<K, V> for PhfMap<K, V> {
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<T: fmt::Show> fmt::Show for PhfMap<T> {
impl<K: fmt::Show, V: fmt::Show> fmt::Show for PhfMap<K, V> {
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, ", "));
}
Expand All @@ -96,13 +99,14 @@ impl<T: fmt::Show> fmt::Show for PhfMap<T> {
}
}

impl<T> PhfMap<T> {
fn find_entry(&self, key: & &str) -> Option<&'static (&'static str, T)> {
let (g, f1, f2) = hash(*key, self.k1, self.k2);
impl<K: Hash+Eq, V> PhfMap<K, V> {
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
Expand All @@ -113,40 +117,64 @@ impl<T> PhfMap<T> {
/// 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<K>>(&'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<K>>(&'a self, key: &T)
-> Option<&'a K> {
self.get_entry(key, |k| key.equiv(k)).map(|e| {
let &(ref k, _) = e;
k
})
}
}

impl<K, V> PhfMap<K, V> {
/// 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<uint>) {
Expand All @@ -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<uint>) {
Expand All @@ -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<uint>) {
Expand All @@ -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",
/// };
Expand All @@ -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<T> {
#[doc(hidden)]
pub map: PhfMap<()>
pub map: PhfMap<T, ()>
}

impl fmt::Show for PhfSet {
impl<T: fmt::Show> fmt::Show for PhfSet<T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
try!(write!(fmt, "{{"));
let mut first = true;
Expand All @@ -229,57 +257,59 @@ impl fmt::Show for PhfSet {
}
}

impl Collection for PhfSet {
impl<T> Collection for PhfSet<T> {
#[inline]
fn len(&self) -> uint {
self.map.len()
}
}

impl<'a> Set<&'a str> for PhfSet {
impl<'a, T: Hash+Eq> Set<T> for PhfSet<T> {
#[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<T>) -> 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<T>) -> bool {
self.iter().all(|value| other.contains(value))
}
}

impl PhfSet {
impl<T: Hash+Eq> PhfSet<T> {
/// 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<T> PhfSet<T> {
/// 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()
}

Expand Down Expand Up @@ -359,7 +389,7 @@ impl<'a, T> Map<&'a str, T> for PhfOrderedMap<T> {

impl<T> PhfOrderedMap<T> {
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];
Expand Down

0 comments on commit cb4ed93

Please sign in to comment.