Skip to content

Commit

Permalink
Merge branch 'qthree-equivalent'
Browse files Browse the repository at this point in the history
  • Loading branch information
wvwwvwwv committed Sep 30, 2024
2 parents ac0dc94 + 2d82b3f commit 7fdb475
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 109 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/scc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ jobs:
- uses: taiki-e/install-action@v2
with:
tool: cargo-msrv, cargo-spellcheck
- name: Clippy
- name: Clippy
run: cargo clippy --all;
cargo clippy -p examples --all
- name: MSRV
Expand All @@ -82,7 +82,9 @@ jobs:
run: cargo fmt --check;
cargo fmt -p examples --check
- name: Doc
run: cargo doc --document-private-items;
run: cargo doc --document-private-items
- name: Loom
run: cargo test --features equivalent --release --lib -- --nocapture
- name: Serde
run: cargo test serde --features serde;
cargo test --release serde --features serde
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ members = [".", "examples"]
loom = { version = "0.7", optional = true }
sdd = "3.0"
serde = { version = "1.0", optional = true }
equivalent = { version = "1.0", optional = true }

[features]
loom = ["dep:loom", "sdd/loom"]
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ A collection of high performance containers and utilities for concurrent and asy
#### Features

- Asynchronous counterparts of blocking and synchronous methods.
- [`Equivalent`](`https://github.com/indexmap-rs/equivalent`) traits support: `features = ["equivalent"]`
- [`Loom`](https://github.com/tokio-rs/loom) and [`Serde`](https://serde.rs) support: `features = ["loom", "serde"]`.
- Near-linear scalability.
- No spin-locks and no busy loops.
Expand Down
37 changes: 37 additions & 0 deletions src/equivalent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//! Vendor the ['equivalent'](https://crates.io/crates/equivalent) crate in order to avoid any conflicts.
use std::{borrow::Borrow, cmp::Ordering};

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

impl<Q: ?Sized, K: ?Sized> Equivalent<K> for Q
where
Q: Eq,
K: Borrow<Q>,
{
#[inline]
fn equivalent(&self, key: &K) -> bool {
PartialEq::eq(self, key.borrow())
}
}

/// Key ordering trait.
pub trait Comparable<K: ?Sized>: Equivalent<K> {
/// Compare self to `key` and return their ordering.
fn compare(&self, key: &K) -> Ordering;
}

impl<Q: ?Sized, K: ?Sized> Comparable<K> for Q
where
Q: Ord,
K: Borrow<Q>,
{
#[inline]
fn compare(&self, key: &K) -> Ordering {
Ord::cmp(self, key.borrow())
}
}
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
67 changes: 27 additions & 40 deletions src/tree_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ mod node;

use crate::ebr::{AtomicShared, Guard, Ptr, Shared, Tag};
use crate::wait_queue::AsyncWait;
use crate::Comparable;
use leaf::{InsertResult, Leaf, RemoveResult, Scanner};
use node::Node;
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::fmt::{self, Debug};
use std::iter::FusedIterator;
use std::marker::PhantomData;
Expand Down Expand Up @@ -325,8 +324,7 @@ where
#[inline]
pub fn remove<Q>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
Q: Ord + ?Sized,
Q: Comparable<K> + ?Sized,
{
self.remove_if(key, |_| true)
}
Expand All @@ -350,8 +348,7 @@ where
#[inline]
pub async fn remove_async<Q>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
Q: Ord + ?Sized,
Q: Comparable<K> + ?Sized,
{
self.remove_if_async(key, |_| true).await
}
Expand All @@ -377,8 +374,7 @@ where
#[inline]
pub fn remove_if<Q, F: FnMut(&V) -> bool>(&self, key: &Q, mut condition: F) -> bool
where
K: Borrow<Q>,
Q: Ord + ?Sized,
Q: Comparable<K> + ?Sized,
{
let mut removed = false;
loop {
Expand Down Expand Up @@ -435,8 +431,7 @@ where
#[inline]
pub async fn remove_if_async<Q, F: FnMut(&V) -> bool>(&self, key: &Q, mut condition: F) -> bool
where
K: Borrow<Q>,
Q: Ord + ?Sized,
Q: Comparable<K> + ?Sized,
{
let mut removed = false;
loop {
Expand Down Expand Up @@ -617,8 +612,7 @@ where
#[inline]
pub fn peek<'g, Q>(&self, key: &Q, guard: &'g Guard) -> Option<&'g V>
where
K: Borrow<Q>,
Q: Ord + ?Sized,
Q: Comparable<K> + ?Sized,
{
if let Some(root_ref) = self.root.load(Acquire, guard).as_ref() {
return root_ref.search_value(key, guard);
Expand Down Expand Up @@ -649,8 +643,7 @@ where
#[inline]
pub fn peek_with<Q, R, F: FnOnce(&K, &V) -> R>(&self, key: &Q, reader: F) -> Option<R>
where
K: Borrow<Q>,
Q: Ord + ?Sized,
Q: Comparable<K> + ?Sized,
{
let guard = Guard::new();
self.peek_entry(key, &guard).map(|(k, v)| reader(k, v))
Expand Down Expand Up @@ -683,8 +676,7 @@ where
#[inline]
pub fn peek_entry<'g, Q>(&self, key: &Q, guard: &'g Guard) -> Option<(&'g K, &'g V)>
where
K: Borrow<Q>,
Q: Ord + ?Sized,
Q: Comparable<K> + ?Sized,
{
if let Some(root_ref) = self.root.load(Acquire, guard).as_ref() {
return root_ref.search_entry(key, guard);
Expand All @@ -708,8 +700,7 @@ where
#[inline]
pub fn contains<Q>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
Q: Ord + ?Sized,
Q: Comparable<K> + ?Sized,
{
self.peek(key, &Guard::new()).is_some()
}
Expand Down Expand Up @@ -797,8 +788,7 @@ where
guard: &'g Guard,
) -> Range<'t, 'g, K, V, Q, R>
where
K: Borrow<Q>,
Q: Ord + ?Sized,
Q: Comparable<K> + ?Sized,
{
Range::new(&self.root, range, guard)
}
Expand Down Expand Up @@ -959,9 +949,9 @@ impl<'t, 'g, K, V, Q: ?Sized, R: RangeBounds<Q>> Range<'t, 'g, K, V, Q, R> {

impl<'t, 'g, K, V, Q, R> Range<'t, 'g, K, V, Q, R>
where
K: 'static + Clone + Ord + Borrow<Q>,
K: 'static + Clone + Ord,
V: 'static + Clone,
Q: ?Sized + Ord,
Q: Comparable<K> + ?Sized,
R: RangeBounds<Q>,
{
#[inline]
Expand Down Expand Up @@ -1003,7 +993,7 @@ where

// Go to the next entry.
if let Some(mut leaf_scanner) = self.leaf_scanner.take() {
let min_allowed_key = leaf_scanner.get().map(|(key, _)| key.borrow());
let min_allowed_key = leaf_scanner.get().map(|(key, _)| key);
if let Some(result) = leaf_scanner.next() {
self.leaf_scanner.replace(leaf_scanner);
return Some(result);
Expand All @@ -1024,14 +1014,12 @@ where
#[inline]
fn set_check_upper_bound(&mut self, scanner: &Scanner<K, V>) {
self.check_upper_bound = match self.range.end_bound() {
Excluded(key) => scanner.max_key().map_or(false, |max_key| {
let kq: &Q = max_key.borrow();
kq.cmp(key) != Ordering::Less
}),
Included(key) => scanner.max_key().map_or(false, |max_key| {
let kq: &Q = max_key.borrow();
kq.cmp(key) == Ordering::Greater
}),
Excluded(key) => scanner
.max_key()
.map_or(false, |max_key| key.compare(max_key).is_le()),
Included(key) => scanner
.max_key()
.map_or(false, |max_key| key.compare(max_key).is_lt()),
Unbounded => false,
};
}
Expand All @@ -1051,26 +1039,25 @@ impl<'t, 'g, K, V, Q: ?Sized, R: RangeBounds<Q>> Debug for Range<'t, 'g, K, V, Q

impl<'t, 'g, K, V, Q, R> Iterator for Range<'t, 'g, K, V, Q, R>
where
K: 'static + Clone + Ord + Borrow<Q>,
K: 'static + Clone + Ord,
V: 'static + Clone,
Q: ?Sized + Ord,
Q: Comparable<K> + ?Sized,
R: RangeBounds<Q>,
{
type Item = (&'g K, &'g V);

#[inline]
fn next(&mut self) -> Option<Self::Item> {
while let Some((k, v)) = self.next_unbounded() {
let kq: &Q = k.borrow();
if self.check_lower_bound {
match self.range.start_bound() {
Excluded(key) => {
if kq.cmp(key) != Ordering::Greater {
if key.compare(k).is_ge() {
continue;
}
}
Included(key) => {
if kq.cmp(key) == Ordering::Less {
if key.compare(k).is_gt() {
continue;
}
}
Expand All @@ -1081,12 +1068,12 @@ where
if self.check_upper_bound {
match self.range.end_bound() {
Excluded(key) => {
if kq.cmp(key) == Ordering::Less {
if key.compare(k).is_gt() {
return Some((k, v));
}
}
Included(key) => {
if kq.cmp(key) != Ordering::Greater {
if key.compare(k).is_ge() {
return Some((k, v));
}
}
Expand All @@ -1104,9 +1091,9 @@ where

impl<'t, 'g, K, V, Q, R> FusedIterator for Range<'t, 'g, K, V, Q, R>
where
K: 'static + Clone + Ord + Borrow<Q>,
K: 'static + Clone + Ord,
V: 'static + Clone,
Q: ?Sized + Ord,
Q: Comparable<K> + ?Sized,
R: RangeBounds<Q>,
{
}
Expand Down
22 changes: 11 additions & 11 deletions src/tree_index/internal_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,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;
Expand Down Expand Up @@ -148,8 +149,8 @@ where
#[inline]
pub(super) fn search_entry<'g, Q>(&self, key: &Q, guard: &'g Guard) -> Option<(&'g K, &'g V)>
where
K: 'g + Borrow<Q>,
Q: Ord + ?Sized,
K: 'g,
Q: Comparable<K> + ?Sized,
{
loop {
let (child, metadata) = self.children.min_greater_equal(key);
Expand Down Expand Up @@ -177,8 +178,8 @@ where
#[inline]
pub(super) fn search_value<'g, Q>(&self, key: &Q, guard: &'g Guard) -> Option<&'g V>
where
K: 'g + Borrow<Q>,
Q: Ord + ?Sized,
K: 'g,
Q: Comparable<K> + ?Sized,
{
loop {
let (child, metadata) = self.children.min_greater_equal(key);
Expand Down Expand Up @@ -246,8 +247,8 @@ where
#[inline]
pub(super) fn max_le_appr<'g, Q>(&self, key: &Q, guard: &'g Guard) -> Option<Scanner<'g, K, V>>
where
K: 'g + Borrow<Q>,
Q: Ord + ?Sized,
K: 'g,
Q: Comparable<K> + ?Sized,
{
loop {
if let Some(scanner) = Scanner::max_less(&self.children, key) {
Expand Down Expand Up @@ -275,7 +276,7 @@ where
min_scanner.next();
loop {
if let Some((k, _)) = min_scanner.get() {
if k.borrow() <= key {
if key.compare(k).is_ge() {
return Some(min_scanner);
}
break;
Expand Down Expand Up @@ -415,8 +416,7 @@ where
guard: &Guard,
) -> Result<RemoveResult, ()>
where
K: Borrow<Q>,
Q: Ord + ?Sized,
Q: Comparable<K> + ?Sized,
D: DeriveAsyncWait,
{
loop {
Expand Down Expand Up @@ -964,8 +964,8 @@ where
#[inline]
pub(super) fn cleanup_link<'g, Q>(&self, key: &Q, traverse_max: bool, guard: &'g Guard) -> bool
where
K: 'g + Borrow<Q>,
Q: Ord + ?Sized,
K: 'g,
Q: Comparable<K> + ?Sized,
{
if traverse_max {
// It just has to search for the maximum leaf node in the tree.
Expand Down
Loading

0 comments on commit 7fdb475

Please sign in to comment.