diff --git a/.gitignore b/.gitignore index bc1484f..1ecbf18 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ /phf/target/ /phf_mac/target/ /doc +target/ +Cargo.lock diff --git a/Makefile.in b/Makefile.in index 3fb2896..1df717e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -8,7 +8,7 @@ HOST_RUSTFLAGS ?= -O PHF_SRC := $(VPATH)/phf/src/lib.rs PHF_MAC_SRC := $(VPATH)/phf_mac/src/lib.rs -PHF_TEST_SRC := $(VPATH)/phf/src/test.rs +PHF_TEST_SRC := $(VPATH)/phf/tests/test.rs all: phf.dummy phf_host.dummy phf_mac.dummy diff --git a/README.md b/README.md index 7241b86..8eceaca 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,6 @@ static KEYWORDS: PhfMap<&'static str, Keyword> = phf_map! { }; pub fn parse_keyword(keyword: &str) -> Option { - KEYWORDS.find(keyword).map(|t| t.clone()) + KEYWORDS.find_equiv(keyword).map(|t| t.clone()) } ``` diff --git a/phf/Cargo.toml b/phf/Cargo.toml index f0471ad..2386374 100644 --- a/phf/Cargo.toml +++ b/phf/Cargo.toml @@ -13,8 +13,3 @@ test = false [dev_dependencies.phf_mac] path = "../phf_mac" - -[[test]] - -name = "test" -path = "src/test.rs" \ No newline at end of file diff --git a/phf/src/lib.rs b/phf/src/lib.rs index 004f11b..b9001b3 100644 --- a/phf/src/lib.rs +++ b/phf/src/lib.rs @@ -59,7 +59,7 @@ impl Collection for PhfMap { } impl<'a, K: Hash+Eq, V> Map for PhfMap { - fn find<'a>(&'a self, key: &K) -> Option<&'a V> { + fn find(&self, key: &K) -> Option<&V> { self.get_entry(key, |k| key == k).map(|e| { let &(_, ref v) = e; v @@ -82,34 +82,42 @@ impl fmt::Show for PhfMap { } } -impl PhfMap { - fn get_entry<'a, T: Hash>(&'a self, key: &T, check: |&K| -> bool) - -> Option<&'a (K, V)> { - let (g, f1, f2) = shared::hash(key, self.k1, self.k2); - let (d1, d2) = self.disps[g % self.disps.len()]; - let entry @ &(ref s, _) = &self.entries[shared::displace(f1, f2, d1, d2) % - self.entries.len()]; - if check(s) { - Some(entry) - } else { - None - } +impl Index for PhfMap { + fn index(&self, k: &K) -> &V { + self.find(k).expect("invalid key") } +} +impl PhfMap { /// Returns a reference to the map's internal static instance of the given /// key. /// /// This can be useful for interning schemes. - pub fn find_key<'a>(&'a self, key: &K) -> Option<&'a K> { + pub fn find_key(&self, key: &K) -> Option<&K> { self.get_entry(key, |k| key == k).map(|e| { let &(ref k, _) = e; k }) } +} + +impl PhfMap { + fn get_entry(&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()]; + let &(ref s, _) = entry; + if check(s) { + Some(entry) + } else { + None + } + } /// 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> { + pub fn find_equiv>(&self, key: &T) -> Option<&V> { self.get_entry(key, |k| key.equiv(k)).map(|e| { let &(_, ref v) = e; v @@ -118,8 +126,7 @@ impl PhfMap { /// 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> { + pub fn find_key_equiv>(&self, key: &T) -> Option<&K> { self.get_entry(key, |k| key.equiv(k)).map(|e| { let &(ref k, _) = e; k @@ -294,11 +301,27 @@ impl PhfSet { /// /// This can be useful for interning schemes. #[inline] - pub fn find_key<'a>(&'a self, key: &T) -> Option<&'a T> { + pub fn find_key(&self, key: &T) -> Option<&T> { self.map.find_key(key) } } +impl PhfSet { + /// Like `contains`, but can operate on any type that is equivalent to a + /// value + #[inline] + pub fn contains_equiv>(&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>(&self, key: &U) -> Option<&T> { + self.map.find_key_equiv(key) + } +} + impl PhfSet { /// Returns an iterator over the values in the set. /// @@ -394,22 +417,42 @@ impl Collection for PhfOrderedMap { } } -impl<'a, K: Hash+Eq, V> Map for PhfOrderedMap { - fn find<'a>(&'a self, key: &K) -> Option<&'a V> { - self.get_entry(key, |k| key == k).map(|e| { +impl Map for PhfOrderedMap { + fn find(&self, key: &K) -> Option<&V> { + self.find_entry(key, |k| k == key).map(|e| { let &(_, ref v) = e; v }) } } +impl Index for PhfOrderedMap { + fn index(&self, k: &K) -> &V { + self.find(k).expect("invalid key") + } +} + impl PhfOrderedMap { - fn get_entry<'a, T: Hash>(&'a self, key: &T, check: |&K| -> bool) - -> Option<&'a (K, V)> { + /// Returns a reference to the map's internal static instance of the given + /// key. + /// + /// This can be useful for interning schemes. + pub fn find_key(&self, key: &K) -> Option<&K> { + self.find_entry(key, |k| k == key).map(|e| { + let &(ref k, _) = e; + k + }) + } +} + +impl PhfOrderedMap { + fn find_entry(&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()]; - let entry @ &(ref s, _) = &self.entries[idx]; + let entry = &self.entries[idx]; + let &(ref s, _) = entry; if check(s) { Some(entry) @@ -418,21 +461,9 @@ impl PhfOrderedMap { } } - /// Returns a reference to the map's internal static instance of the given - /// key. - /// - /// This can be useful for interning schemes. - 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| { + pub fn find_equiv>(&self, key: &T) -> Option<&V> { + self.find_entry(key, |k| key.equiv(k)).map(|e| { let &(_, ref v) = e; v }) @@ -440,9 +471,8 @@ impl PhfOrderedMap { /// 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| { + pub fn find_key_equiv>(&self, key: &T) -> Option<&K> { + self.find_entry(key, |k| key.equiv(k)).map(|e| { let &(ref k, _) = e; k }) @@ -651,12 +681,26 @@ impl PhfOrderedSet { /// /// This can be useful for interning schemes. #[inline] - pub fn find_key<'a>(&'a self, key: &T) -> Option<&'a T> { + pub fn find_key(&self, key: &T) -> Option<&T> { self.map.find_key(key) } } impl PhfOrderedSet { + /// Like `contains`, but can operate on any type that is equivalent to a + /// value + #[inline] + pub fn contains_equiv>(&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>(&self, key: &U) -> Option<&T> { + self.map.find_key_equiv(key) + } + /// Returns an iterator over the values in the set. /// /// Values are returned in the same order in which they were defined. diff --git a/phf/src/test.rs b/phf/tests/test.rs similarity index 84% rename from phf/src/test.rs rename to phf/tests/test.rs index ee95a99..edfa789 100644 --- a/phf/src/test.rs +++ b/phf/tests/test.rs @@ -115,6 +115,23 @@ mod map { assert_eq!(Some(&0), map.find_equiv(&"a".to_string().as_slice())); } + #[test] + fn test_index_ok() { + static map: PhfMap<&'static str, int> = phf_map!( + "a" => 0, + ); + assert_eq!(0, map["a"]); + } + + #[test] + #[should_fail] + fn test_index_fail() { + static map: PhfMap<&'static str, int> = phf_map!( + "a" => 0, + ); + map["b"]; + } + macro_rules! test_key_type( ($t:ty, $($k:expr => $v:expr),+) => ({ static map: PhfMap<$t, int> = phf_map! { @@ -224,6 +241,15 @@ mod set { assert!(set.contains(&"world")); assert_eq!(2, set.len()); } + + #[test] + fn test_non_static_str_contains() { + static SET: PhfSet<&'static str> = phf_set! { + "hello", + "world", + }; + assert!(SET.contains_equiv(&"hello".to_string().as_slice())); + } } mod ordered_map { @@ -283,6 +309,31 @@ mod ordered_map { let vec = MAP.values().map(|&v| v).collect::>(); assert_eq!(vec, vec!(10i, 11, 12)); } + + #[test] + fn test_index_ok() { + static map: PhfOrderedMap<&'static str, int> = phf_ordered_map!( + "a" => 0, + ); + assert_eq!(0, map["a"]); + } + + #[test] + #[should_fail] + fn test_index_fail() { + static map: PhfOrderedMap<&'static str, int> = phf_ordered_map!( + "a" => 0, + ); + map["b"]; + } + + #[test] + fn test_non_static_str_key() { + static map: PhfOrderedMap<&'static str, int> = phf_ordered_map!( + "a" => 0, + ); + assert_eq!(Some(&0), map.find_equiv(&"a".to_string().as_slice())); + } } mod ordered_set { @@ -322,4 +373,13 @@ mod ordered_set { let vec = SET.iter().map(|&e| e).collect::>(); assert_eq!(vec, vec!("hello", "there", "world")); } + + #[test] + fn test_non_static_str_contains() { + static SET: PhfOrderedSet<&'static str> = phf_ordered_set! { + "hello", + "world", + }; + assert!(SET.contains_equiv(&"hello".to_string().as_slice())); + } } diff --git a/phf_mac/src/lib.rs b/phf_mac/src/lib.rs index d519be4..e92fbaf 100644 --- a/phf_mac/src/lib.rs +++ b/phf_mac/src/lib.rs @@ -24,6 +24,7 @@ use syntax::ext::base::{DummyResult, ExtCtxt, MacResult, MacExpr}; +use syntax::fold::Folder; use syntax::parse; use syntax::parse::token::{InternedString, COMMA, EOF, FAT_ARROW}; use syntax::print::pprust; @@ -165,8 +166,8 @@ fn parse_map(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option> { let mut bad = false; while parser.token != EOF { - let key = cx.expand_expr(parser.parse_expr()); - let key_contents = parse_key(cx, key).unwrap_or_else(|| { + let key = cx.expander().fold_expr(parser.parse_expr()); + let key_contents = parse_key(cx, &*key).unwrap_or_else(|| { bad = true; KeyStr(InternedString::new("")) }); @@ -212,8 +213,8 @@ fn parse_set(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option> { let mut bad = false; while parser.token != EOF { - let key = cx.expand_expr(parser.parse_expr()); - let key_contents = parse_key(cx, key).unwrap_or_else(|| { + let key = cx.expander().fold_expr(parser.parse_expr()); + let key_contents = parse_key(cx, &*key).unwrap_or_else(|| { bad = true; KeyStr(InternedString::new("")) }); @@ -366,11 +367,11 @@ fn try_generate_hash(entries: &[Entry], rng: &mut XorShiftRng) 'disps: for d2 in range(0, table_len) { try_map.clear(); for &key in bucket.keys.iter() { - let idx = shared::displace(hashes.get(key).f1, - hashes.get(key).f2, + let idx = shared::displace(hashes[key].f1, + hashes[key].f2, d1, d2) % table_len; - if map.get(idx).is_some() || try_map.find(&idx).is_some() { + if map[idx].is_some() || try_map.find(&idx).is_some() { continue 'disps; } try_map.insert(idx, key); @@ -405,7 +406,7 @@ fn create_map(cx: &mut ExtCtxt, sp: Span, entries: Vec, state: HashState) let disps = create_slice_expr(disps, sp); let entries = state.map.iter().map(|&idx| { - let &Entry { key, value, .. } = entries.get(idx); + let &Entry { key, value, .. } = &entries[idx]; quote_expr!(&*cx, ($key, $value)) }).collect(); let entries = create_slice_expr(entries, sp);