From c13ef0eb09141c0e01bbe4767771724acd2c9844 Mon Sep 17 00:00:00 2001 From: al8n Date: Mon, 9 Dec 2024 10:47:33 +0800 Subject: [PATCH] flip `K` and `Q` --- crossbeam-skiplist/Cargo.toml | 1 - crossbeam-skiplist/src/base.rs | 125 +++++++++++++++++-------------- crossbeam-skiplist/src/lib.rs | 50 +++++++++++++ crossbeam-skiplist/src/map.rs | 38 ++++++---- crossbeam-skiplist/src/set.rs | 37 +++++---- crossbeam-skiplist/tests/base.rs | 36 ++++----- 6 files changed, 177 insertions(+), 110 deletions(-) diff --git a/crossbeam-skiplist/Cargo.toml b/crossbeam-skiplist/Cargo.toml index d85bfa8c8..75a0df46b 100644 --- a/crossbeam-skiplist/Cargo.toml +++ b/crossbeam-skiplist/Cargo.toml @@ -28,7 +28,6 @@ std = ["alloc", "crossbeam-epoch/std", "crossbeam-utils/std"] alloc = ["crossbeam-epoch/alloc"] [dependencies] -equivalent = "1" crossbeam-epoch = { version = "0.9.17", path = "../crossbeam-epoch", default-features = false } crossbeam-utils = { version = "0.8.18", path = "../crossbeam-utils", default-features = false } diff --git a/crossbeam-skiplist/src/base.rs b/crossbeam-skiplist/src/base.rs index 314f227ae..d14147c48 100644 --- a/crossbeam-skiplist/src/base.rs +++ b/crossbeam-skiplist/src/base.rs @@ -1,7 +1,7 @@ //! A lock-free skip list. See [`SkipList`]. +use super::Comparable; use alloc::alloc::{alloc, dealloc, handle_alloc_error, Layout}; -use core::borrow::Borrow; use core::cmp; use core::fmt; use core::marker::PhantomData; @@ -9,7 +9,6 @@ use core::mem; use core::ops::{Bound, Deref, Index, RangeBounds}; use core::ptr; use core::sync::atomic::{fence, AtomicUsize, Ordering}; -use equivalent::Comparable; use crossbeam_epoch::{self as epoch, Atomic, Collector, Guard, Shared}; use crossbeam_utils::CachePadded; @@ -410,7 +409,8 @@ where /// Returns `true` if the map contains a value for the specified key. pub fn contains_key(&self, key: &Q, guard: &Guard) -> bool where - Q: ?Sized + Comparable, + K: Comparable, + Q: ?Sized, { self.get(key, guard).is_some() } @@ -418,13 +418,15 @@ where /// Returns an entry with the specified `key`. pub fn get<'a: 'g, 'g, Q>(&'a self, key: &Q, guard: &'g Guard) -> Option> where - Q: ?Sized + Comparable, + K: Comparable, + Q: ?Sized, { self.check_guard(guard); let n = self.search_bound(Bound::Included(key), false, guard)?; - if key.compare(&n.key).is_ne() { + if !n.key.equivalent(key) { return None; } + Some(Entry { parent: self, node: n, @@ -441,7 +443,8 @@ where guard: &'g Guard, ) -> Option> where - Q: ?Sized + Comparable, + K: Comparable, + Q: ?Sized, { self.check_guard(guard); let n = self.search_bound(bound, false, guard)?; @@ -461,7 +464,8 @@ where guard: &'g Guard, ) -> Option> where - Q: ?Sized + Comparable, + K: Comparable, + Q: ?Sized, { self.check_guard(guard); let n = self.search_bound(bound, true, guard)?; @@ -519,9 +523,9 @@ where guard: &'g Guard, ) -> Range<'a, 'g, Q, R, K, V> where - K: Borrow, + K: Comparable, R: RangeBounds, - Q: ?Sized + Comparable, + Q: ?Sized, { self.check_guard(guard); Range { @@ -538,8 +542,9 @@ where #[allow(clippy::needless_lifetimes)] pub fn ref_range<'a, Q, R>(&'a self, range: R) -> RefRange<'a, Q, R, K, V> where + K: Comparable, R: RangeBounds, - Q: ?Sized + Comparable, + Q: ?Sized, { RefRange { parent: self, @@ -681,7 +686,8 @@ where guard: &'a Guard, ) -> Option<&'a Node> where - Q: ?Sized + Comparable, + K: Comparable, + Q: ?Sized, { unsafe { 'search: loop { @@ -739,11 +745,11 @@ where // bound, we return the last node before the condition became true. For the // lower bound, we return the first node after the condition became true. if upper_bound { - if !below_upper_bound(&bound, c.key.borrow()) { + if !below_upper_bound(&bound, &c.key) { break; } result = Some(c); - } else if above_lower_bound(&bound, c.key.borrow()) { + } else if above_lower_bound(&bound, &c.key) { result = Some(c); break; } @@ -762,7 +768,8 @@ where /// Searches for a key in the skip list and returns a list of all adjacent nodes. fn search_position<'a, Q>(&'a self, key: &Q, guard: &'a Guard) -> Position<'a, K, V> where - Q: ?Sized + Comparable, + K: Comparable, + Q: ?Sized, { unsafe { 'search: loop { @@ -819,7 +826,7 @@ where // If `curr` contains a key that is greater than or equal to `key`, we're // done with this level. - match key.compare(&c.key).reverse() { + match c.key.compare(key) { cmp::Ordering::Greater => break, cmp::Ordering::Equal => { result.found = Some(c); @@ -1095,7 +1102,8 @@ where /// Removes an entry with the specified `key` from the map and returns it. pub fn remove(&self, key: &Q, guard: &Guard) -> Option> where - Q: ?Sized + Comparable, + K: Comparable, + Q: ?Sized, { self.check_guard(guard); @@ -1788,9 +1796,9 @@ impl<'a, K: 'a, V: 'a> RefIter<'a, K, V> { /// An iterator over a subset of entries of a `SkipList`. pub struct Range<'a: 'g, 'g, Q, R, K, V> where - K: Ord, + K: Ord + Comparable, R: RangeBounds, - Q: ?Sized + Comparable, + Q: ?Sized, { parent: &'a SkipList, head: Option<&'g Node>, @@ -1802,9 +1810,9 @@ where impl<'a: 'g, 'g, Q, R, K: 'a, V: 'a> Iterator for Range<'a, 'g, Q, R, K, V> where - K: Ord, + K: Ord + Comparable, R: RangeBounds, - Q: ?Sized + Comparable, + Q: ?Sized, { type Item = Entry<'a, 'g, K, V>; @@ -1845,15 +1853,15 @@ where impl<'a: 'g, 'g, Q, R, K: 'a, V: 'a> DoubleEndedIterator for Range<'a, 'g, Q, R, K, V> where - K: Ord, + K: Ord + Comparable, R: RangeBounds, - Q: ?Sized + Comparable, + Q: ?Sized, { fn next_back(&mut self) -> Option> { self.tail = match self.tail { Some(n) => self .parent - .search_bound(Bound::Excluded(n.key.borrow()), true, self.guard), + .search_bound::(Bound::Excluded(&n.key), true, self.guard), None => self .parent .search_bound(self.range.end_bound(), true, self.guard), @@ -1861,7 +1869,7 @@ where if let Some(t) = self.tail { match self.head { Some(h) => { - let bound = Bound::Excluded(h.key.borrow()); + let bound = Bound::Excluded(&h.key); if !above_lower_bound(&bound, &t.key) { self.head = None; self.tail = None; @@ -1886,10 +1894,10 @@ where impl fmt::Debug for Range<'_, '_, Q, R, K, V> where - K: Ord + fmt::Debug, + K: Ord + fmt::Debug + Comparable, V: fmt::Debug, R: RangeBounds + fmt::Debug, - Q: ?Sized + Comparable, + Q: ?Sized, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Range") @@ -1903,8 +1911,9 @@ where /// An iterator over reference-counted subset of entries of a `SkipList`. pub struct RefRange<'a, Q, R, K, V> where + K: Ord + Comparable, R: RangeBounds, - Q: ?Sized + Comparable, + Q: ?Sized, { parent: &'a SkipList, pub(crate) head: Option>, @@ -1915,24 +1924,26 @@ where unsafe impl Send for RefRange<'_, Q, R, K, V> where + K: Ord + Comparable, R: RangeBounds, - Q: ?Sized + Comparable, + Q: ?Sized, { } unsafe impl Sync for RefRange<'_, Q, R, K, V> where + K: Ord + Comparable, R: RangeBounds, - Q: ?Sized + Comparable, + Q: ?Sized, { } impl fmt::Debug for RefRange<'_, Q, R, K, V> where - K: fmt::Debug, + K: Ord + fmt::Debug + Comparable, V: fmt::Debug, R: RangeBounds + fmt::Debug, - Q: ?Sized + Comparable, + Q: ?Sized, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RefRange") @@ -1945,26 +1956,9 @@ where impl<'a, Q, R, K: 'a, V: 'a> RefRange<'a, Q, R, K, V> where + K: Ord + Comparable, R: RangeBounds, - Q: ?Sized + Comparable, -{ - /// Decrements a reference count owned by this iterator. - pub fn drop_impl(&mut self, guard: &Guard) { - self.parent.check_guard(guard); - if let Some(e) = self.head.take() { - unsafe { e.node.decrement(guard) }; - } - if let Some(e) = self.tail.take() { - unsafe { e.node.decrement(guard) }; - } - } -} - -impl<'a, Q, R, K: 'a, V: 'a> RefRange<'a, Q, R, K, V> -where - K: Ord, - R: RangeBounds, - Q: ?Sized + Comparable, + Q: ?Sized, { /// Advances the iterator and returns the next value. pub fn next(&mut self, guard: &Guard) -> Option> { @@ -2045,6 +2039,17 @@ where None } } + + /// Decrements a reference count owned by this iterator. + pub fn drop_impl(&mut self, guard: &Guard) { + self.parent.check_guard(guard); + if let Some(e) = self.head.take() { + unsafe { e.node.decrement(guard) }; + } + if let Some(e) = self.tail.take() { + unsafe { e.node.decrement(guard) }; + } + } } /// An owning iterator over the entries of a `SkipList`. @@ -2129,19 +2134,27 @@ where } /// Helper function to check if a value is above a lower bound -fn above_lower_bound>(bound: &Bound<&T>, other: &V) -> bool { +fn above_lower_bound(bound: &Bound<&T>, other: &V) -> bool +where + T: ?Sized, + V: Comparable, +{ match *bound { Bound::Unbounded => true, - Bound::Included(key) => key.compare(other).is_le(), - Bound::Excluded(key) => key.compare(other).is_lt(), + Bound::Included(key) => other.compare(key).is_ge(), + Bound::Excluded(key) => other.compare(key).is_gt(), } } /// Helper function to check if a value is below an upper bound -fn below_upper_bound>(bound: &Bound<&T>, other: &V) -> bool { +fn below_upper_bound(bound: &Bound<&T>, other: &V) -> bool +where + T: ?Sized, + V: Comparable, +{ match *bound { Bound::Unbounded => true, - Bound::Included(key) => key.compare(other).is_ge(), - Bound::Excluded(key) => key.compare(other).is_gt(), + Bound::Included(key) => other.compare(key).is_le(), + Bound::Excluded(key) => other.compare(key).is_lt(), } } diff --git a/crossbeam-skiplist/src/lib.rs b/crossbeam-skiplist/src/lib.rs index e5d137fbe..45a9e7365 100644 --- a/crossbeam-skiplist/src/lib.rs +++ b/crossbeam-skiplist/src/lib.rs @@ -245,6 +245,8 @@ extern crate std; #[cfg(all(feature = "alloc", target_has_atomic = "ptr"))] pub mod base; +use core::{borrow::Borrow, cmp::Ordering}; + #[cfg(all(feature = "alloc", target_has_atomic = "ptr"))] #[doc(inline)] pub use crate::base::SkipList; @@ -257,3 +259,51 @@ pub mod set; #[cfg(feature = "std")] #[doc(inline)] pub use crate::{map::SkipMap, set::SkipSet}; + +/// Key equivalence trait. +/// +/// This trait allows hash table lookup to be customized. It has one blanket +/// implementation that uses the regular solution with `Borrow` and `Eq`, just +/// like `HashMap` does, so that you can pass `&str` to lookup into a map with +/// `String` keys and so on. +/// +/// # Contract +/// +/// The implementor **must** hash like `Q`, if it is hashable. +pub trait Equivalent { + /// Compare self to `key` and return `true` if they are equal. + fn equivalent(&self, key: &Q) -> bool; +} + +impl Equivalent for K +where + K: Borrow, + Q: Eq, +{ + #[inline] + fn equivalent(&self, key: &Q) -> bool { + PartialEq::eq(self.borrow(), key) + } +} + +/// Key ordering trait. +/// +/// This trait allows ordered map lookup to be customized. It has one blanket +/// implementation that uses the regular solution with `Borrow` and `Ord`, just +/// like `BTreeMap` does, so that you can pass `&str` to lookup into a map with +/// `String` keys and so on. +pub trait Comparable: Equivalent { + /// Compare self to `key` and return their ordering. + fn compare(&self, key: &Q) -> Ordering; +} + +impl Comparable for K +where + K: Borrow, + Q: Ord, +{ + #[inline] + fn compare(&self, key: &Q) -> Ordering { + Ord::cmp(self.borrow(), key) + } +} diff --git a/crossbeam-skiplist/src/map.rs b/crossbeam-skiplist/src/map.rs index 353a1d8ef..8fc45deed 100644 --- a/crossbeam-skiplist/src/map.rs +++ b/crossbeam-skiplist/src/map.rs @@ -5,9 +5,9 @@ use std::mem::ManuallyDrop; use std::ops::{Bound, RangeBounds}; use std::ptr; +use super::Comparable; use crate::base::{self, try_pin_loop}; use crossbeam_epoch as epoch; -use equivalent::Comparable; /// An ordered map based on a lock-free skip list. /// @@ -133,7 +133,8 @@ where /// ``` pub fn contains_key(&self, key: &Q) -> bool where - Q: ?Sized + Comparable, + K: Comparable, + Q: ?Sized, { let guard = &epoch::pin(); self.inner.contains_key(key, guard) @@ -156,7 +157,8 @@ where /// ``` pub fn get(&self, key: &Q) -> Option> where - Q: ?Sized + Comparable, + K: Comparable, + Q: ?Sized, { let guard = &epoch::pin(); try_pin_loop(|| self.inner.get(key, guard)).map(Entry::new) @@ -190,7 +192,8 @@ where /// ``` pub fn lower_bound<'a, Q>(&'a self, bound: Bound<&Q>) -> Option> where - Q: ?Sized + Comparable, + K: Comparable, + Q: ?Sized, { let guard = &epoch::pin(); try_pin_loop(|| self.inner.lower_bound(bound, guard)).map(Entry::new) @@ -221,7 +224,8 @@ where /// ``` pub fn upper_bound<'a, Q>(&'a self, bound: Bound<&Q>) -> Option> where - Q: ?Sized + Comparable, + K: Comparable, + Q: ?Sized, { let guard = &epoch::pin(); try_pin_loop(|| self.inner.upper_bound(bound, guard)).map(Entry::new) @@ -334,7 +338,8 @@ where pub fn range(&self, range: R) -> Range<'_, Q, R, K, V> where R: RangeBounds, - Q: ?Sized + Comparable, + K: Comparable, + Q: ?Sized, { Range { inner: self.inner.ref_range(range), @@ -419,7 +424,8 @@ where /// ``` pub fn remove(&self, key: &Q) -> Option> where - Q: ?Sized + Comparable, + K: Comparable, + Q: ?Sized, { let guard = &epoch::pin(); self.inner.remove(key, guard).map(Entry::new) @@ -715,17 +721,18 @@ impl Drop for Iter<'_, K, V> { /// An iterator over a subset of entries of a `SkipMap`. pub struct Range<'a, Q, R, K, V> where + K: Ord + Comparable, R: RangeBounds, - Q: ?Sized + Comparable, + Q: ?Sized, { pub(crate) inner: base::RefRange<'a, Q, R, K, V>, } impl<'a, Q, R, K, V> Iterator for Range<'a, Q, R, K, V> where - K: Ord, + K: Ord + Comparable, R: RangeBounds, - Q: ?Sized + Comparable, + Q: ?Sized, { type Item = Entry<'a, K, V>; @@ -737,9 +744,9 @@ where impl<'a, Q, R, K, V> DoubleEndedIterator for Range<'a, Q, R, K, V> where - K: Ord, + K: Ord + Comparable, R: RangeBounds, - Q: ?Sized + Comparable, + Q: ?Sized, { fn next_back(&mut self) -> Option> { let guard = &epoch::pin(); @@ -749,10 +756,10 @@ where impl fmt::Debug for Range<'_, Q, R, K, V> where - K: fmt::Debug, + K: Ord + fmt::Debug + Comparable, V: fmt::Debug, R: RangeBounds + fmt::Debug, - Q: ?Sized + Comparable, + Q: ?Sized, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Range") @@ -765,8 +772,9 @@ where impl Drop for Range<'_, Q, R, K, V> where + K: Ord + Comparable, R: RangeBounds, - Q: ?Sized + Comparable, + Q: ?Sized, { fn drop(&mut self) { let guard = &epoch::pin(); diff --git a/crossbeam-skiplist/src/set.rs b/crossbeam-skiplist/src/set.rs index b61b496e0..2dbc9ad20 100644 --- a/crossbeam-skiplist/src/set.rs +++ b/crossbeam-skiplist/src/set.rs @@ -4,7 +4,7 @@ use std::fmt; use std::ops::Deref; use std::ops::{Bound, RangeBounds}; -use equivalent::Comparable; +use super::Comparable; use crate::map; @@ -123,7 +123,8 @@ where /// ``` pub fn contains(&self, key: &Q) -> bool where - Q: ?Sized + Comparable, + T: Comparable, + Q: ?Sized, { self.inner.contains_key(key) } @@ -141,7 +142,8 @@ where /// ``` pub fn get(&self, key: &Q) -> Option> where - Q: ?Sized + Comparable, + T: Comparable, + Q: ?Sized, { self.inner.get(key).map(Entry::new) } @@ -172,7 +174,8 @@ where /// ``` pub fn lower_bound<'a, Q>(&'a self, bound: Bound<&Q>) -> Option> where - Q: ?Sized + Comparable, + T: Comparable, + Q: ?Sized, { self.inner.lower_bound(bound).map(Entry::new) } @@ -200,7 +203,8 @@ where /// ``` pub fn upper_bound<'a, Q>(&'a self, bound: Bound<&Q>) -> Option> where - Q: ?Sized + Comparable, + T: Comparable, + Q: ?Sized, { self.inner.upper_bound(bound).map(Entry::new) } @@ -264,7 +268,8 @@ where pub fn range(&self, range: R) -> Range<'_, Q, R, T> where R: RangeBounds, - Q: ?Sized + Comparable, + T: Comparable, + Q: ?Sized, { Range { inner: self.inner.range(range), @@ -311,7 +316,8 @@ where /// ``` pub fn remove(&self, key: &Q) -> Option> where - Q: ?Sized + Comparable, + T: Comparable, + Q: ?Sized, { self.inner.remove(key).map(Entry::new) } @@ -450,7 +456,7 @@ impl<'a, T> Entry<'a, T> { } /// Returns a reference to the value. - pub fn value(&self) -> &'a T { + pub fn value(&self) -> &T { self.inner.key() } @@ -577,17 +583,18 @@ impl fmt::Debug for Iter<'_, T> { /// An iterator over a subset of entries of a `SkipSet`. pub struct Range<'a, Q, R, T> where + T: Ord + Comparable, R: RangeBounds, - Q: ?Sized + Comparable, + Q: ?Sized, { inner: map::Range<'a, Q, R, T, ()>, } impl<'a, Q, R, T> Iterator for Range<'a, Q, R, T> where - T: Ord, + T: Ord + Comparable, R: RangeBounds, - Q: ?Sized + Comparable, + Q: ?Sized, { type Item = Entry<'a, T>; @@ -598,9 +605,9 @@ where impl<'a, Q, R, T> DoubleEndedIterator for Range<'a, Q, R, T> where - T: Ord, + T: Ord + Comparable, R: RangeBounds, - Q: ?Sized + Comparable, + Q: ?Sized, { fn next_back(&mut self) -> Option> { self.inner.next_back().map(Entry::new) @@ -609,9 +616,9 @@ where impl fmt::Debug for Range<'_, Q, R, T> where - T: fmt::Debug, + T: Ord + Comparable + fmt::Debug, R: RangeBounds + fmt::Debug, - Q: ?Sized + Comparable, + Q: ?Sized, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Range") diff --git a/crossbeam-skiplist/tests/base.rs b/crossbeam-skiplist/tests/base.rs index 7af6fa1c5..161c13343 100644 --- a/crossbeam-skiplist/tests/base.rs +++ b/crossbeam-skiplist/tests/base.rs @@ -281,12 +281,7 @@ fn lower_bound() { s.insert(40, 4, guard).release(guard); s.insert(20, 2, guard).release(guard); - assert_eq!( - *s.lower_bound::(Bound::Unbounded, guard) - .unwrap() - .value(), - 1 - ); + assert_eq!(*s.lower_bound(Bound::Unbounded, guard).unwrap().value(), 1); assert_eq!( *s.lower_bound(Bound::Included(&10), guard).unwrap().value(), @@ -366,12 +361,7 @@ fn upper_bound() { s.insert(40, 4, guard).release(guard); s.insert(20, 2, guard).release(guard); - assert_eq!( - *s.upper_bound::(Bound::Unbounded, guard) - .unwrap() - .value(), - 5 - ); + assert_eq!(*s.upper_bound(Bound::Unbounded, guard).unwrap().value(), 5); assert_eq!( *s.upper_bound(Bound::Included(&10), guard).unwrap().value(), @@ -913,7 +903,7 @@ fn drops() { #[test] fn comparable_get() { - use equivalent::{Comparable, Equivalent}; + use crossbeam_skiplist::{Comparable, Equivalent}; #[derive(PartialEq, Eq, PartialOrd, Ord)] struct Foo { @@ -945,19 +935,19 @@ fn comparable_get() { } } - impl Equivalent for FooRef<'_> { - fn equivalent(&self, key: &Foo) -> bool { - let a = u64::from_be_bytes(self.data[..8].try_into().unwrap()); - let b = u32::from_be_bytes(self.data[8..].try_into().unwrap()); - a == key.a && b == key.b + impl<'a> Equivalent> for Foo { + fn equivalent(&self, key: &FooRef<'a>) -> bool { + let a = u64::from_be_bytes(key.data[..8].try_into().unwrap()); + let b = u32::from_be_bytes(key.data[8..].try_into().unwrap()); + a == self.a && b == self.b } } - impl Comparable for FooRef<'_> { - fn compare(&self, key: &Foo) -> std::cmp::Ordering { - let a = u64::from_be_bytes(self.data[..8].try_into().unwrap()); - let b = u32::from_be_bytes(self.data[8..].try_into().unwrap()); - Foo { a, b }.cmp(key) + impl<'a> Comparable> for Foo { + fn compare(&self, key: &FooRef<'a>) -> std::cmp::Ordering { + let a = u64::from_be_bytes(key.data[..8].try_into().unwrap()); + let b = u32::from_be_bytes(key.data[8..].try_into().unwrap()); + Foo { a, b }.cmp(self) } }