diff --git a/crossbeam-skiplist/src/base.rs b/crossbeam-skiplist/src/base.rs index f3c90b1f9..2b85a2bb8 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::equivalent::Comparable; use alloc::alloc::{alloc, dealloc, handle_alloc_error, Layout}; -use core::borrow::Borrow; use core::cmp; use core::fmt; use core::marker::PhantomData; @@ -398,7 +398,7 @@ where /// Returns the entry with the largest key. pub fn back<'a: 'g, 'g>(&'a self, guard: &'g Guard) -> Option> { self.check_guard(guard); - let n = self.search_bound(Bound::Unbounded, true, guard)?; + let n = self.search_bound::(Bound::Unbounded, true, guard)?; Some(Entry { parent: self, node: n, @@ -409,8 +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 - K: Borrow, - Q: Ord + ?Sized, + K: Comparable, + Q: ?Sized, { self.get(key, guard).is_some() } @@ -418,14 +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 - K: Borrow, - Q: Ord + ?Sized, + K: Comparable, + Q: ?Sized, { self.check_guard(guard); let n = self.search_bound(Bound::Included(key), false, guard)?; - if n.key.borrow() != key { + if !n.key.equivalent(key) { return None; } + Some(Entry { parent: self, node: n, @@ -442,8 +443,8 @@ where guard: &'g Guard, ) -> Option> where - K: Borrow, - Q: Ord + ?Sized, + K: Comparable, + Q: ?Sized, { self.check_guard(guard); let n = self.search_bound(bound, false, guard)?; @@ -463,8 +464,8 @@ where guard: &'g Guard, ) -> Option> where - K: Borrow, - Q: Ord + ?Sized, + K: Comparable, + Q: ?Sized, { self.check_guard(guard); let n = self.search_bound(bound, true, guard)?; @@ -522,9 +523,9 @@ where guard: &'g Guard, ) -> Range<'a, 'g, Q, R, K, V> where - K: Borrow, + K: Comparable, R: RangeBounds, - Q: Ord + ?Sized, + Q: ?Sized, { self.check_guard(guard); Range { @@ -541,9 +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: Borrow, + K: Comparable, R: RangeBounds, - Q: Ord + ?Sized, + Q: ?Sized, { RefRange { parent: self, @@ -685,8 +686,8 @@ where guard: &'a Guard, ) -> Option<&'a Node> where - K: Borrow, - Q: Ord + ?Sized, + K: Comparable, + Q: ?Sized, { unsafe { 'search: loop { @@ -744,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; } @@ -767,8 +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 - K: Borrow, - Q: Ord + ?Sized, + K: Comparable, + Q: ?Sized, { unsafe { 'search: loop { @@ -825,7 +826,7 @@ where // If `curr` contains a key that is greater than or equal to `key`, we're // done with this level. - match c.key.borrow().cmp(key) { + match c.key.compare(key) { cmp::Ordering::Greater => break, cmp::Ordering::Equal => { result.found = Some(c); @@ -1101,8 +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 - K: Borrow, - Q: Ord + ?Sized, + K: Comparable, + Q: ?Sized, { self.check_guard(guard); @@ -1208,7 +1209,7 @@ where // By unlinking nodes in batches we make sure that the final search doesn't // unlink all nodes at once, which could keep the current thread pinned for a // long time. - let mut entry = self.lower_bound(Bound::Unbounded, guard); + let mut entry = self.lower_bound::(Bound::Unbounded, guard); for _ in 0..BATCH_SIZE { // Stop if we have reached the end of the list. @@ -1663,7 +1664,9 @@ where Some(n) => self .parent .search_bound(Bound::Excluded(&n.key), true, self.guard), - None => self.parent.search_bound(Bound::Unbounded, true, self.guard), + None => self + .parent + .search_bound::(Bound::Unbounded, true, self.guard), }; if let (Some(h), Some(t)) = (self.head, self.tail) { if h.key >= t.key { @@ -1793,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 + Borrow, + K: Ord + Comparable, R: RangeBounds, - Q: Ord + ?Sized, + Q: ?Sized, { parent: &'a SkipList, head: Option<&'g Node>, @@ -1807,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 + Borrow, + K: Ord + Comparable, R: RangeBounds, - Q: Ord + ?Sized, + Q: ?Sized, { type Item = Entry<'a, 'g, K, V>; @@ -1823,14 +1826,22 @@ where .search_bound(self.range.start_bound(), false, self.guard), }; if let Some(h) = self.head { - let bound = match self.tail { - Some(t) => Bound::Excluded(t.key.borrow()), - None => self.range.end_bound(), + match self.tail { + Some(t) => { + let bound = Bound::Excluded(&t.key); + if !below_upper_bound(&bound, &h.key) { + self.head = None; + self.tail = None; + } + } + None => { + let bound = self.range.end_bound(); + if !below_upper_bound(&bound, &h.key) { + self.head = None; + self.tail = None; + } + } }; - if !below_upper_bound(&bound, h.key.borrow()) { - self.head = None; - self.tail = None; - } } self.head.map(|n| Entry { parent: self.parent, @@ -1842,28 +1853,36 @@ where impl<'a: 'g, 'g, Q, R, K: 'a, V: 'a> DoubleEndedIterator for Range<'a, 'g, Q, R, K, V> where - K: Ord + Borrow, + K: Ord + Comparable, R: RangeBounds, - Q: Ord + ?Sized, + 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), }; if let Some(t) = self.tail { - let bound = match self.head { - Some(h) => Bound::Excluded(h.key.borrow()), - None => self.range.start_bound(), + match self.head { + Some(h) => { + let bound = Bound::Excluded(&h.key); + if !above_lower_bound(&bound, &t.key) { + self.head = None; + self.tail = None; + } + } + None => { + let bound = self.range.start_bound(); + if !above_lower_bound(&bound, &t.key) { + self.head = None; + self.tail = None; + } + } }; - if !above_lower_bound(&bound, t.key.borrow()) { - self.head = None; - self.tail = None; - } } self.tail.map(|n| Entry { parent: self.parent, @@ -1875,10 +1894,10 @@ where impl fmt::Debug for Range<'_, '_, Q, R, K, V> where - K: Ord + Borrow + fmt::Debug, + K: Ord + fmt::Debug + Comparable, V: fmt::Debug, R: RangeBounds + fmt::Debug, - Q: Ord + ?Sized, + Q: ?Sized, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Range") @@ -1892,9 +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 + Borrow, + K: Ord + Comparable, R: RangeBounds, - Q: Ord + ?Sized, + Q: ?Sized, { parent: &'a SkipList, pub(crate) head: Option>, @@ -1905,26 +1924,26 @@ where unsafe impl Send for RefRange<'_, Q, R, K, V> where - K: Ord + Borrow, + K: Ord + Comparable, R: RangeBounds, - Q: Ord + ?Sized, + Q: ?Sized, { } unsafe impl Sync for RefRange<'_, Q, R, K, V> where - K: Ord + Borrow, + K: Ord + Comparable, R: RangeBounds, - Q: Ord + ?Sized, + Q: ?Sized, { } impl fmt::Debug for RefRange<'_, Q, R, K, V> where - K: Ord + Borrow + fmt::Debug, + K: Ord + fmt::Debug + Comparable, V: fmt::Debug, R: RangeBounds + fmt::Debug, - Q: Ord + ?Sized, + Q: ?Sized, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RefRange") @@ -1937,9 +1956,9 @@ where impl<'a, Q, R, K: 'a, V: 'a> RefRange<'a, Q, R, K, V> where - K: Ord + Borrow, + K: Ord + Comparable, R: RangeBounds, - Q: Ord + ?Sized, + Q: ?Sized, { /// Advances the iterator and returns the next value. pub fn next(&mut self, guard: &Guard) -> Option> { @@ -1950,18 +1969,31 @@ where }; if let Some(ref h) = next_head { - let bound = match self.tail { - Some(ref t) => Bound::Excluded(t.key().borrow()), - None => self.range.end_bound(), - }; - if below_upper_bound(&bound, h.key().borrow()) { - self.head.clone_from(&next_head); - next_head - } else { - unsafe { - h.node.decrement(guard); + match self.tail { + Some(ref t) => { + let bound = Bound::Excluded(t.key()); + if below_upper_bound(&bound, h.key()) { + self.head.clone_from(&next_head); + next_head + } else { + unsafe { + h.node.decrement(guard); + } + None + } + } + None => { + let bound = self.range.end_bound(); + if below_upper_bound(&bound, h.key()) { + self.head.clone_from(&next_head); + next_head + } else { + unsafe { + h.node.decrement(guard); + } + None + } } - None } } else { None @@ -1977,18 +2009,31 @@ where }; if let Some(ref t) = next_tail { - let bound = match self.head { - Some(ref h) => Bound::Excluded(h.key().borrow()), - None => self.range.start_bound(), - }; - if above_lower_bound(&bound, t.key().borrow()) { - self.tail.clone_from(&next_tail); - next_tail - } else { - unsafe { - t.node.decrement(guard); + match self.head { + Some(ref h) => { + let bound = Bound::Excluded(h.key()); + if above_lower_bound(&bound, t.key()) { + self.tail.clone_from(&next_tail); + next_tail + } else { + unsafe { + t.node.decrement(guard); + } + None + } + } + None => { + let bound = self.range.start_bound(); + if above_lower_bound(&bound, t.key()) { + self.tail.clone_from(&next_tail); + next_tail + } else { + unsafe { + t.node.decrement(guard); + } + None + } } - None } } else { None @@ -2089,19 +2134,27 @@ where } /// Helper function to check if a value is above a lower bound -fn above_lower_bound(bound: &Bound<&T>, other: &T) -> bool { +fn above_lower_bound(bound: &Bound<&T>, other: &V) -> bool +where + T: ?Sized, + V: Comparable, +{ match *bound { Bound::Unbounded => true, - Bound::Included(key) => other >= key, - Bound::Excluded(key) => other > key, + 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: &T) -> bool { +fn below_upper_bound(bound: &Bound<&T>, other: &V) -> bool +where + T: ?Sized, + V: Comparable, +{ match *bound { Bound::Unbounded => true, - Bound::Included(key) => other <= key, - Bound::Excluded(key) => other < key, + Bound::Included(key) => other.compare(key).is_le(), + Bound::Excluded(key) => other.compare(key).is_lt(), } } diff --git a/crossbeam-skiplist/src/equivalent.rs b/crossbeam-skiplist/src/equivalent.rs new file mode 100644 index 000000000..0a1577256 --- /dev/null +++ b/crossbeam-skiplist/src/equivalent.rs @@ -0,0 +1,54 @@ +// These traits are based on `equivalent` crate, but `K` and `Q` are flipped to avoid type inference issues: +// https://github.com/indexmap-rs/equivalent/issues/5 + +//! Traits for key comparison in maps. + +use core::{borrow::Borrow, cmp::Ordering}; + +/// 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/lib.rs b/crossbeam-skiplist/src/lib.rs index e5d137fbe..f045f6ddb 100644 --- a/crossbeam-skiplist/src/lib.rs +++ b/crossbeam-skiplist/src/lib.rs @@ -245,6 +245,7 @@ extern crate std; #[cfg(all(feature = "alloc", target_has_atomic = "ptr"))] pub mod base; + #[cfg(all(feature = "alloc", target_has_atomic = "ptr"))] #[doc(inline)] pub use crate::base::SkipList; @@ -257,3 +258,5 @@ pub mod set; #[cfg(feature = "std")] #[doc(inline)] pub use crate::{map::SkipMap, set::SkipSet}; + +pub mod equivalent; diff --git a/crossbeam-skiplist/src/map.rs b/crossbeam-skiplist/src/map.rs index e131e79a9..9502662d3 100644 --- a/crossbeam-skiplist/src/map.rs +++ b/crossbeam-skiplist/src/map.rs @@ -1,12 +1,14 @@ //! An ordered map based on a lock-free skip list. See [`SkipMap`]. -use std::borrow::Borrow; use std::fmt; use std::mem::ManuallyDrop; use std::ops::{Bound, RangeBounds}; use std::ptr; -use crate::base::{self, try_pin_loop}; +use crate::{ + base::{self, try_pin_loop}, + equivalent::Comparable, +}; use crossbeam_epoch as epoch; /// An ordered map based on a lock-free skip list. @@ -133,8 +135,8 @@ where /// ``` pub fn contains_key(&self, key: &Q) -> bool where - K: Borrow, - Q: Ord + ?Sized, + K: Comparable, + Q: ?Sized, { let guard = &epoch::pin(); self.inner.contains_key(key, guard) @@ -157,8 +159,8 @@ where /// ``` pub fn get(&self, key: &Q) -> Option> where - K: Borrow, - Q: Ord + ?Sized, + K: Comparable, + Q: ?Sized, { let guard = &epoch::pin(); try_pin_loop(|| self.inner.get(key, guard)).map(Entry::new) @@ -192,8 +194,8 @@ where /// ``` pub fn lower_bound<'a, Q>(&'a self, bound: Bound<&Q>) -> Option> where - K: Borrow, - Q: Ord + ?Sized, + K: Comparable, + Q: ?Sized, { let guard = &epoch::pin(); try_pin_loop(|| self.inner.lower_bound(bound, guard)).map(Entry::new) @@ -224,8 +226,8 @@ where /// ``` pub fn upper_bound<'a, Q>(&'a self, bound: Bound<&Q>) -> Option> where - K: Borrow, - Q: Ord + ?Sized, + K: Comparable, + Q: ?Sized, { let guard = &epoch::pin(); try_pin_loop(|| self.inner.upper_bound(bound, guard)).map(Entry::new) @@ -337,9 +339,9 @@ where /// ``` pub fn range(&self, range: R) -> Range<'_, Q, R, K, V> where - K: Borrow, R: RangeBounds, - Q: Ord + ?Sized, + K: Comparable, + Q: ?Sized, { Range { inner: self.inner.ref_range(range), @@ -424,8 +426,8 @@ where /// ``` pub fn remove(&self, key: &Q) -> Option> where - K: Borrow, - Q: Ord + ?Sized, + K: Comparable, + Q: ?Sized, { let guard = &epoch::pin(); self.inner.remove(key, guard).map(Entry::new) @@ -721,18 +723,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 + Borrow, + K: Ord + Comparable, R: RangeBounds, - Q: Ord + ?Sized, + 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 + Borrow, + K: Ord + Comparable, R: RangeBounds, - Q: Ord + ?Sized, + Q: ?Sized, { type Item = Entry<'a, K, V>; @@ -744,9 +746,9 @@ where impl<'a, Q, R, K, V> DoubleEndedIterator for Range<'a, Q, R, K, V> where - K: Ord + Borrow, + K: Ord + Comparable, R: RangeBounds, - Q: Ord + ?Sized, + Q: ?Sized, { fn next_back(&mut self) -> Option> { let guard = &epoch::pin(); @@ -756,10 +758,10 @@ where impl fmt::Debug for Range<'_, Q, R, K, V> where - K: Ord + Borrow + fmt::Debug, + K: Ord + fmt::Debug + Comparable, V: fmt::Debug, R: RangeBounds + fmt::Debug, - Q: Ord + ?Sized, + Q: ?Sized, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Range") @@ -772,9 +774,9 @@ where impl Drop for Range<'_, Q, R, K, V> where - K: Ord + Borrow, + K: Ord + Comparable, R: RangeBounds, - Q: Ord + ?Sized, + 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 b185ff8ba..96ac46e96 100644 --- a/crossbeam-skiplist/src/set.rs +++ b/crossbeam-skiplist/src/set.rs @@ -1,11 +1,10 @@ //! A set based on a lock-free skip list. See [`SkipSet`]. -use std::borrow::Borrow; use std::fmt; use std::ops::Deref; use std::ops::{Bound, RangeBounds}; -use crate::map; +use crate::{equivalent::Comparable, map}; /// A set based on a lock-free skip list. /// @@ -122,8 +121,8 @@ where /// ``` pub fn contains(&self, key: &Q) -> bool where - T: Borrow, - Q: Ord + ?Sized, + T: Comparable, + Q: ?Sized, { self.inner.contains_key(key) } @@ -141,8 +140,8 @@ where /// ``` pub fn get(&self, key: &Q) -> Option> where - T: Borrow, - Q: Ord + ?Sized, + T: Comparable, + Q: ?Sized, { self.inner.get(key).map(Entry::new) } @@ -173,8 +172,8 @@ where /// ``` pub fn lower_bound<'a, Q>(&'a self, bound: Bound<&Q>) -> Option> where - T: Borrow, - Q: Ord + ?Sized, + T: Comparable, + Q: ?Sized, { self.inner.lower_bound(bound).map(Entry::new) } @@ -202,8 +201,8 @@ where /// ``` pub fn upper_bound<'a, Q>(&'a self, bound: Bound<&Q>) -> Option> where - T: Borrow, - Q: Ord + ?Sized, + T: Comparable, + Q: ?Sized, { self.inner.upper_bound(bound).map(Entry::new) } @@ -266,9 +265,9 @@ where /// ``` pub fn range(&self, range: R) -> Range<'_, Q, R, T> where - T: Borrow, R: RangeBounds, - Q: Ord + ?Sized, + T: Comparable, + Q: ?Sized, { Range { inner: self.inner.range(range), @@ -315,8 +314,8 @@ where /// ``` pub fn remove(&self, key: &Q) -> Option> where - T: Borrow, - Q: Ord + ?Sized, + T: Comparable, + Q: ?Sized, { self.inner.remove(key).map(Entry::new) } @@ -582,18 +581,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 + Borrow, + T: Ord + Comparable, R: RangeBounds, - Q: Ord + ?Sized, + Q: ?Sized, { inner: map::Range<'a, Q, R, T, ()>, } impl<'a, Q, R, T> Iterator for Range<'a, Q, R, T> where - T: Ord + Borrow, + T: Ord + Comparable, R: RangeBounds, - Q: Ord + ?Sized, + Q: ?Sized, { type Item = Entry<'a, T>; @@ -604,9 +603,9 @@ where impl<'a, Q, R, T> DoubleEndedIterator for Range<'a, Q, R, T> where - T: Ord + Borrow, + T: Ord + Comparable, R: RangeBounds, - Q: Ord + ?Sized, + Q: ?Sized, { fn next_back(&mut self) -> Option> { self.inner.next_back().map(Entry::new) @@ -615,9 +614,9 @@ where impl fmt::Debug for Range<'_, Q, R, T> where - T: Ord + Borrow + fmt::Debug, + T: Ord + Comparable + fmt::Debug, R: RangeBounds + fmt::Debug, - Q: Ord + ?Sized, + 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 83d2f221e..3f717a6b9 100644 --- a/crossbeam-skiplist/tests/base.rs +++ b/crossbeam-skiplist/tests/base.rs @@ -900,3 +900,73 @@ fn drops() { assert_eq!(KEYS.load(Ordering::SeqCst), 8); assert_eq!(VALUES.load(Ordering::SeqCst), 7); } + +#[test] +fn comparable_get() { + use crossbeam_skiplist::equivalent::{Comparable, Equivalent}; + + #[derive(PartialEq, Eq, PartialOrd, Ord)] + struct Foo { + a: u64, + b: u32, + } + + #[derive(PartialEq, Eq)] + struct FooRef<'a> { + data: &'a [u8], + } + + impl PartialOrd for FooRef<'_> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } + } + + impl Ord for FooRef<'_> { + fn cmp(&self, other: &Self) -> 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()); + let other_a = u64::from_be_bytes(other.data[..8].try_into().unwrap()); + let other_b = u32::from_be_bytes(other.data[8..].try_into().unwrap()); + Foo { a, b }.cmp(&Foo { + a: other_a, + b: other_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<'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) + } + } + + let s = SkipList::new(epoch::default_collector().clone()); + let foo = Foo { a: 1, b: 2 }; + + let g = &epoch::pin(); + s.insert(foo, 12, g); + + let buf = 1u64 + .to_be_bytes() + .iter() + .chain(2u32.to_be_bytes().iter()) + .copied() + .collect::>(); + let foo_ref = FooRef { data: &buf }; + + let ent = s.get(&foo_ref, g).unwrap(); + assert_eq!(ent.key().a, 1); + assert_eq!(ent.key().b, 2); + assert_eq!(*ent.value(), 12); +}