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
Note: It's a type inference break for the case of ``.remove(&&K)`` which
previously compiled (due to deref coercion?)
  • Loading branch information
bluss committed Sep 11, 2017
1 parent 0ffc75b commit fc0dd18
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 31 deletions.
2 changes: 1 addition & 1 deletion benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ fn remove_ordermap_100_000(b: &mut Bencher) {
b.iter(|| {
let mut map = map.clone();
for key in &keys {
map.remove(&key).is_some();
map.remove(key).is_some();
}
assert_eq!(map.len(), 0);
map
Expand Down
27 changes: 27 additions & 0 deletions src/equivalent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

use std::borrow::Borrow;

/// Key equivalence 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`, if applicable.
pub trait Equivalent<K> {
/// Compare self to `key` and return `true` if they are equal.
fn equivalent(&self, key: &K) -> bool;
}

impl<Q: ?Sized, K> Equivalent<K> for Q
where Q: Eq,
K: Borrow<Q>,
{
#[inline]
fn equivalent(&self, key: &K) -> bool {
*self == *key.borrow()
}
}
48 changes: 18 additions & 30 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ mod macros;
#[cfg(feature = "serde-1")]
mod serde;
mod util;
mod equivalent;

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

use std::cmp::{max, Ordering};
Expand All @@ -19,6 +19,7 @@ use std::mem::{replace};
use std::marker::PhantomData;

use util::{second, ptrdistance, enumerate};
pub use equivalent::Equivalent;

fn hash_elem_using<B: BuildHasher, K: ?Sized + Hash>(build: &B, k: &K) -> HashValue {
let mut h = build.build_hasher();
Expand Down Expand Up @@ -824,8 +825,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: Hash + Equivalent<K>,
{
self.find(key).is_some()
}
Expand All @@ -835,15 +835,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: Hash + Equivalent<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: Hash + Equivalent<K>,
{
if let Some((_, found)) = self.find(key) {
let entry = &self.entries[found];
Expand All @@ -855,8 +853,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: Hash + Equivalent<K>,
{
if let Some((_, found)) = self.find(key) {
let entry = &self.entries[found];
Expand All @@ -867,16 +864,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: Hash + Equivalent<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: Hash + Equivalent<K>,
{
if let Some((_, found)) = self.find(key) {
let entry = &mut self.entries[found];
Expand All @@ -888,8 +883,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: Hash + Equivalent<K>,
{
if let Some((_, found)) = self.find(key) {
let entry = &mut self.entries[found];
Expand All @@ -901,12 +895,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: Hash + Equivalent<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::equivalent(key, &entry.key) })
}

/// Remove the key-value pair equivalent to `key` and return
Expand All @@ -920,8 +913,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: Hash + Equivalent<K>,
{
self.swap_remove_pair(key).map(second)
}
Expand All @@ -930,8 +922,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: Hash + Equivalent<K>,
{
self.swap_remove(key)
}
Expand All @@ -944,8 +935,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: Hash + Equivalent<K>,
{
let (probe, found) = match self.find(key) {
None => return None,
Expand Down Expand Up @@ -1470,9 +1460,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: Hash + Equivalent<K>,
K: Hash + Eq,
S: BuildHasher,
{
type Output = V;
Expand All @@ -1492,9 +1481,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: Hash + Equivalent<K>,
K: Hash + Eq,
S: BuildHasher,
{
/// ***Panics*** if `key` is not present in the map.
Expand Down
42 changes: 42 additions & 0 deletions tests/equivalent_trait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

#[macro_use] extern crate ordermap;

use ordermap::Equivalent;

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> Equivalent<X> for Pair<A, B>
where Pair<A, B>: PartialEq<X>,
A: Hash + Eq,
B: Hash + Eq,
{
fn equivalent(&self, other: &X) -> bool {
*self == *other
}
}

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

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

0 comments on commit fc0dd18

Please sign in to comment.