Skip to content

Commit

Permalink
Abstract key lookup into a trait, so that it can be extended
Browse files Browse the repository at this point in the history
  • Loading branch information
bluss committed Nov 14, 2016
1 parent f46145b commit f4d4a33
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 30 deletions.
48 changes: 18 additions & 30 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@

mod macros;
mod util;
mod lookup;

use std::hash::Hash;
use std::hash::BuildHasher;
use std::hash::Hasher;
use std::collections::hash_map::RandomState;
use std::borrow::Borrow;

use std::cmp::max;
use std::fmt;
use std::mem::{replace};
use std::marker::PhantomData;

use util::{second, ptrdistance, enumerate};
pub use lookup::Lookup;

fn hash_elem_using<B: BuildHasher, K: ?Sized + Hash>(build: &B, k: &K) -> HashValue {
let mut h = build.build_hasher();
Expand Down Expand Up @@ -787,8 +788,7 @@ impl<K, V, S> OrderMap<K, V, S>
///
/// Computes in **O(1)** time (average).
pub fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool
where K: Borrow<Q>,
Q: Eq + Hash,
where Q: Lookup<K>,
{
self.find(key).is_some()
}
Expand All @@ -798,15 +798,13 @@ impl<K, V, S> OrderMap<K, V, S>
///
/// Computes in **O(1)** time (average).
pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&V>
where K: Borrow<Q>,
Q: Eq + Hash,
where Q: Lookup<K>,
{
self.get_pair(key).map(second)
}

pub fn get_pair<Q: ?Sized>(&self, key: &Q) -> Option<(&K, &V)>
where K: Borrow<Q>,
Q: Eq + Hash,
where Q: Lookup<K>,
{
if let Some((_, found)) = self.find(key) {
let entry = &self.entries[found];
Expand All @@ -818,8 +816,7 @@ impl<K, V, S> OrderMap<K, V, S>

/// Return item index, key and value
pub fn get_pair_index<Q: ?Sized>(&self, key: &Q) -> Option<(usize, &K, &V)>
where K: Borrow<Q>,
Q: Eq + Hash,
where Q: Lookup<K>,
{
if let Some((_, found)) = self.find(key) {
let entry = &self.entries[found];
Expand All @@ -830,16 +827,14 @@ impl<K, V, S> OrderMap<K, V, S>
}

pub fn get_mut<Q: ?Sized>(&mut self, key: &Q) -> Option<&mut V>
where K: Borrow<Q>,
Q: Eq + Hash,
where Q: Lookup<K>,
{
self.get_pair_mut(key).map(second)
}

pub fn get_pair_mut<Q: ?Sized>(&mut self, key: &Q)
-> Option<(&mut K, &mut V)>
where K: Borrow<Q>,
Q: Eq + Hash,
where Q: Lookup<K>,
{
if let Some((_, found)) = self.find(key) {
let entry = &mut self.entries[found];
Expand All @@ -851,8 +846,7 @@ impl<K, V, S> OrderMap<K, V, S>

pub fn get_pair_index_mut<Q: ?Sized>(&mut self, key: &Q)
-> Option<(usize, &mut K, &mut V)>
where K: Borrow<Q>,
Q: Eq + Hash,
where Q: Lookup<K>,
{
if let Some((_, found)) = self.find(key) {
let entry = &mut self.entries[found];
Expand All @@ -864,12 +858,11 @@ impl<K, V, S> OrderMap<K, V, S>

/// Return probe (indices) and position (entries)
fn find<Q: ?Sized>(&self, key: &Q) -> Option<(usize, usize)>
where K: Borrow<Q>,
Q: Eq + Hash,
where Q: Lookup<K>,
{
if self.len() == 0 { return None; }
let h = hash_elem_using(&self.hash_builder, key);
self.find_using(h, move |entry| { *entry.key.borrow() == *key })
self.find_using(h, move |entry| { Q::equal(key, &entry.key) })
}

/// Remove the key-value pair equivalent to `key` and return
Expand All @@ -883,8 +876,7 @@ impl<K, V, S> OrderMap<K, V, S>
///
/// Computes in **O(1)** time (average).
pub fn swap_remove<Q: ?Sized>(&mut self, key: &Q) -> Option<V>
where K: Borrow<Q>,
Q: Eq + Hash,
where Q: Lookup<K>,
{
self.swap_remove_pair(key).map(second)
}
Expand All @@ -893,8 +885,7 @@ impl<K, V, S> OrderMap<K, V, S>
///
/// Computes in **O(1)** time (average).
pub fn remove<Q: ?Sized>(&mut self, key: &Q) -> Option<V>
where K: Borrow<Q>,
Q: Eq + Hash,
where Q: Lookup<K>,
{
self.swap_remove(key)
}
Expand All @@ -907,8 +898,7 @@ impl<K, V, S> OrderMap<K, V, S>
///
/// Return `None` if `key` is not in map.
pub fn swap_remove_pair<Q: ?Sized>(&mut self, key: &Q) -> Option<(K, V)>
where K: Borrow<Q>,
Q: Eq + Hash,
where Q: Lookup<K>,
{
let (probe, found) = match self.find(key) {
None => return None,
Expand Down Expand Up @@ -1306,9 +1296,8 @@ impl<K, V, S> IntoIterator for OrderMap<K, V, S>
use std::ops::{Index, IndexMut};

impl<'a, K, V, Q: ?Sized, S> Index<&'a Q> for OrderMap<K, V, S>
where K: Eq + Hash,
K: Borrow<Q>,
Q: Eq + Hash,
where Q: Lookup<K>,
K: Hash + Eq,
S: BuildHasher,
{
type Output = V;
Expand All @@ -1328,9 +1317,8 @@ impl<'a, K, V, Q: ?Sized, S> Index<&'a Q> for OrderMap<K, V, S>
///
/// You can **not** insert new pairs with index syntax, use `.insert()`.
impl<'a, K, V, Q: ?Sized, S> IndexMut<&'a Q> for OrderMap<K, V, S>
where K: Eq + Hash,
K: Borrow<Q>,
Q: Eq + Hash,
where Q: Lookup<K>,
K: Hash + Eq,
S: BuildHasher,
{
/// ***Panics*** if `key` is not present in the map.
Expand Down
28 changes: 28 additions & 0 deletions src/lookup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

use std::hash::Hash;
use std::borrow::Borrow;

/// Key lookup trait.
///
/// This trait allows hash table lookup to be customized.
/// It has one blanket implementation that uses the regular `Borrow` solution,
/// just like `HashMap` and `BTreeMap` do, so that you can pass `&str` to lookup
/// into a map with `String` keys and so on.
///
/// # Contract
///
/// The implementor must hash like `K`.
pub trait Lookup<K: ?Sized> : Hash {
/// Compare self to `key` and return `true` if they are equal.
fn equal(&self, key: &K) -> bool;
}

impl<Q: ?Sized, K: ?Sized> Lookup<K> for Q
where Q: Eq + Hash,
K: Borrow<Q>,
{
#[inline]
fn equal(&self, key: &K) -> bool {
*self == *key.borrow()
}
}
41 changes: 41 additions & 0 deletions tests/lookup_trait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

#[macro_use] extern crate ordermap;

use ordermap::{Lookup};

use std::hash::{Hash};

#[derive(Debug, Hash)]
pub struct Pair<A, B>(pub A, pub B);

impl<A, B, C, D> PartialEq<(A, B)> for Pair<C, D>
where C: PartialEq<A>,
D: PartialEq<B>,
{
fn eq(&self, rhs: &(A, B)) -> bool {
self.0 == rhs.0 &&
self.1 == rhs.1 &&
true
}
}

impl<A, B, X: ?Sized> Lookup<X> for Pair<A, B>
where Pair<A, B>: PartialEq<X>,
A: Hash + Eq,
B: Hash + Eq,
{
fn equal(&self, other: &X) -> bool {
*self == *other
}
}

#[test]
fn test_lookup() {
let map = ordermap! {
(String::from("a"), String::from("b")) => 1,
(String::from("a"), String::from("x")) => 2,
};

assert!(map.contains_key(&Pair("a", "b")));
assert!(!map.contains_key(&Pair("b", "a")));
}

0 comments on commit f4d4a33

Please sign in to comment.