diff --git a/Cargo.toml b/Cargo.toml index 2a0e163..8533220 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ members = [".", "examples"] loom = { version = "0.7", optional = true } sdd = "3.0" serde = { version = "1.0", optional = true } -equivalent = "1.0.1" +equivalent = { version = "1.0.1", optional = true } [features] loom = ["dep:loom", "sdd/loom"] diff --git a/README.md b/README.md index b244bfb..a267808 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ A collection of high performance containers and utilities for concurrent and asy - Near-linear scalability. - No spin-locks and no busy loops. - SIMD lookup to scan multiple entries in parallel [^note]. +- [`equivalent`](`https://github.com/indexmap-rs/equivalent`) traits support: `features = ["equivalent"]` [^note]: Advanced SIMD instructions are used only when respective target features are enabled, e.g., `-C target_feature=+avx2`. diff --git a/src/equivalent.rs b/src/equivalent.rs new file mode 100644 index 0000000..dfb207a --- /dev/null +++ b/src/equivalent.rs @@ -0,0 +1,63 @@ +use std::{borrow::Borrow, cmp::Ordering}; + +/// Key equivalence trait. +/// +/// This trait defines the function used to compare the input value with the +/// map keys (or set values) during a lookup operation such as [`HashMap::get`](crate::HashMap::get) +/// or [`HashSet::contains`](crate::HashSet::contains). +/// It is provided with a blanket implementation based on the +/// [`Borrow`](core::borrow::Borrow) trait. +/// +/// # Correctness +/// +/// The implementor **must** hash like `K`, if it is hashable. +/// +/// # Credits +/// +/// Inspired by `equivalent` and `hashbrown` crates +pub trait Equivalent { + /// Compare self to `key` and return `true` if they are equal. + /// + /// # Correctness + /// + /// When this function returns `true`, both `self` and `key` must hash to + /// the same value. + fn equivalent(&self, key: &K) -> bool; +} + +impl Equivalent for Q +where + Q: Eq, + K: Borrow, +{ + #[inline] + fn equivalent(&self, key: &K) -> bool { + PartialEq::eq(self, key.borrow()) + } +} + +/// Key ordering trait. +/// +/// This trait defines the function used to compare the input value with the [`TreeIndex`](crate::TreeIndex) keys +/// during a lookup operations such as [`TreeIndex::peek`](crate::TreeIndex::peek) and [`TreeIndex::range`](crate::TreeIndex::range) +/// It is provided with a blanket implementation based on the +/// [`Borrow`](core::borrow::Borrow) trait. +/// +/// /// # Credits +/// +/// Inspired by `equivalent` crate +pub trait Comparable: Equivalent { + /// Compare self to `key` and return their ordering. + fn compare(&self, key: &K) -> Ordering; +} + +impl Comparable for Q +where + Q: Ord, + K: Borrow, +{ + #[inline] + fn compare(&self, key: &K) -> Ordering { + Ord::cmp(self, key.borrow()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 428f980..bae321c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,6 +36,11 @@ mod exit_guard; mod hash_table; mod wait_queue; +#[cfg(not(feature = "equivalent"))] +mod equivalent; + +pub use equivalent::{Comparable, Equivalent}; + #[cfg(feature = "loom")] mod maybe_std { pub(crate) use loom::sync::atomic::{AtomicU8, AtomicUsize}; diff --git a/src/tree_index.rs b/src/tree_index.rs index 3f808b5..b9c854e 100644 --- a/src/tree_index.rs +++ b/src/tree_index.rs @@ -7,7 +7,7 @@ mod node; use crate::ebr::{AtomicShared, Guard, Ptr, Shared, Tag}; use crate::wait_queue::AsyncWait; -use equivalent::Comparable; +use crate::Comparable; use leaf::{InsertResult, Leaf, RemoveResult, Scanner}; use node::Node; use std::fmt::{self, Debug}; diff --git a/src/tree_index/internal_node.rs b/src/tree_index/internal_node.rs index 4d4cd16..98f9cd4 100644 --- a/src/tree_index/internal_node.rs +++ b/src/tree_index/internal_node.rs @@ -1,5 +1,3 @@ -use equivalent::Comparable; - use super::leaf::{InsertResult, Leaf, RemoveResult, Scanner, DIMENSION}; use super::leaf_node::RemoveRangeState; use super::leaf_node::{LOCKED, RETIRED}; @@ -8,6 +6,7 @@ use crate::ebr::{AtomicShared, Guard, Ptr, Shared, Tag}; use crate::exit_guard::ExitGuard; use crate::maybe_std::AtomicU8; use crate::wait_queue::{DeriveAsyncWait, WaitQueue}; +use crate::Comparable; use std::borrow::Borrow; use std::cmp::Ordering::{Equal, Greater, Less}; use std::mem::forget; diff --git a/src/tree_index/leaf.rs b/src/tree_index/leaf.rs index 85a5f35..024a27a 100644 --- a/src/tree_index/leaf.rs +++ b/src/tree_index/leaf.rs @@ -1,7 +1,6 @@ -use equivalent::Comparable; - use crate::ebr::{AtomicShared, Guard, Shared}; use crate::maybe_std::AtomicUsize; +use crate::Comparable; use crate::LinkedList; use std::cell::UnsafeCell; use std::cmp::Ordering; diff --git a/src/tree_index/leaf_node.rs b/src/tree_index/leaf_node.rs index 695754f..12524eb 100644 --- a/src/tree_index/leaf_node.rs +++ b/src/tree_index/leaf_node.rs @@ -1,5 +1,3 @@ -use equivalent::Comparable; - use super::leaf::{InsertResult, RemoveResult, Scanner, DIMENSION}; use super::node::Node; use super::Leaf; @@ -7,6 +5,7 @@ use crate::ebr::{AtomicShared, Guard, Ptr, Shared, Tag}; use crate::exit_guard::ExitGuard; use crate::maybe_std::AtomicU8; use crate::wait_queue::{DeriveAsyncWait, WaitQueue}; +use crate::Comparable; use crate::LinkedList; use std::borrow::Borrow; use std::cmp::Ordering::{Equal, Greater, Less}; diff --git a/src/tree_index/node.rs b/src/tree_index/node.rs index 24f6416..05c3c31 100644 --- a/src/tree_index/node.rs +++ b/src/tree_index/node.rs @@ -1,10 +1,9 @@ -use equivalent::Comparable; - use super::internal_node::{self, InternalNode}; use super::leaf::{InsertResult, Leaf, RemoveResult, Scanner}; use super::leaf_node::{self, LeafNode}; use crate::ebr::{AtomicShared, Guard, Ptr, Shared, Tag}; use crate::wait_queue::DeriveAsyncWait; +use crate::Comparable; use std::fmt::{self, Debug}; use std::ops::RangeBounds; use std::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release};