diff --git a/src/arc.rs b/src/arc.rs index 0abb26c..ecc5582 100644 --- a/src/arc.rs +++ b/src/arc.rs @@ -942,4 +942,90 @@ mod tests { assert_eq!([17, 19], *arc); assert_eq!(1, Arc::count(&arc)); } + + #[test] + fn arc_eq_and_cmp() { + [ + [("*", &b"AB"[..]), ("*", &b"ab"[..])], + [("*", &b"AB"[..]), ("*", &b"a"[..])], + [("*", &b"A"[..]), ("*", &b"ab"[..])], + [("A", &b"*"[..]), ("a", &b"*"[..])], + [("a", &b"*"[..]), ("A", &b"*"[..])], + [("AB", &b"*"[..]), ("a", &b"*"[..])], + [("A", &b"*"[..]), ("ab", &b"*"[..])], + ] + .iter() + .for_each(|[lt @ (lh, ls), rt @ (rh, rs)]| { + let l = Arc::from_header_and_slice(lh, ls); + let r = Arc::from_header_and_slice(rh, rs); + + assert_eq!(l, l); + assert_eq!(r, r); + + assert_ne!(l, r); + assert_ne!(r, l); + + assert_eq!(l <= l, lt <= lt, "{lt:?} <= {lt:?}"); + assert_eq!(l >= l, lt >= lt, "{lt:?} >= {lt:?}"); + + assert_eq!(l < l, lt < lt, "{lt:?} < {lt:?}"); + assert_eq!(l > l, lt > lt, "{lt:?} > {lt:?}"); + + assert_eq!(r <= r, rt <= rt, "{rt:?} <= {rt:?}"); + assert_eq!(r >= r, rt >= rt, "{rt:?} >= {rt:?}"); + + assert_eq!(r < r, rt < rt, "{rt:?} < {rt:?}"); + assert_eq!(r > r, rt > rt, "{rt:?} > {rt:?}"); + + assert_eq!(l < r, lt < rt, "{lt:?} < {rt:?}"); + assert_eq!(r > l, rt > lt, "{rt:?} > {lt:?}"); + }) + } + + #[test] + fn arc_eq_and_partial_cmp() { + [ + [(0.0, &[0.0, 0.0][..]), (1.0, &[0.0, 0.0][..])], + [(1.0, &[0.0, 0.0][..]), (0.0, &[0.0, 0.0][..])], + [(0.0, &[0.0][..]), (0.0, &[0.0, 0.0][..])], + [(0.0, &[0.0, 0.0][..]), (0.0, &[0.0][..])], + [(0.0, &[1.0, 2.0][..]), (0.0, &[10.0, 20.0][..])], + ] + .iter() + .for_each(|[lt @ (lh, ls), rt @ (rh, rs)]| { + let l = Arc::from_header_and_slice(lh, ls); + let r = Arc::from_header_and_slice(rh, rs); + + assert_eq!(l, l); + assert_eq!(r, r); + + assert_ne!(l, r); + assert_ne!(r, l); + + assert_eq!(l <= l, lt <= lt, "{lt:?} <= {lt:?}"); + assert_eq!(l >= l, lt >= lt, "{lt:?} >= {lt:?}"); + + assert_eq!(l < l, lt < lt, "{lt:?} < {lt:?}"); + assert_eq!(l > l, lt > lt, "{lt:?} > {lt:?}"); + + assert_eq!(r <= r, rt <= rt, "{rt:?} <= {rt:?}"); + assert_eq!(r >= r, rt >= rt, "{rt:?} >= {rt:?}"); + + assert_eq!(r < r, rt < rt, "{rt:?} < {rt:?}"); + assert_eq!(r > r, rt > rt, "{rt:?} > {rt:?}"); + + assert_eq!(l < r, lt < rt, "{lt:?} < {rt:?}"); + assert_eq!(r > l, rt > lt, "{rt:?} > {lt:?}"); + }) + } + + #[allow(dead_code)] + const fn is_partial_ord() {} + + #[allow(dead_code)] + const fn is_ord() {} + + // compile-time check that PartialOrd/Ord is correctly derived + const _: () = is_partial_ord::>(); + const _: () = is_ord::>(); } diff --git a/src/header.rs b/src/header.rs index 6bc2d82..403f0ba 100644 --- a/src/header.rs +++ b/src/header.rs @@ -2,6 +2,7 @@ use alloc::alloc::Layout; use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; +use core::cmp::Ordering; use core::iter::{ExactSizeIterator, Iterator}; use core::marker::PhantomData; use core::mem::{self, ManuallyDrop}; @@ -12,7 +13,7 @@ use super::{Arc, ArcInner}; /// Structure to allow Arc-managing some fixed-sized data and a variably-sized /// slice in a single allocation. -#[derive(Debug, Eq, PartialEq, Hash, PartialOrd)] +#[derive(Debug, Eq, PartialEq, Hash, PartialOrd, Ord)] #[repr(C)] pub struct HeaderSlice { /// The fixed-sized data. @@ -153,7 +154,7 @@ impl Arc> { /// Header data with an inline length. Consumers that use HeaderWithLength as the /// Header type in HeaderSlice can take advantage of ThinArc. -#[derive(Debug, Eq, PartialEq, Hash, PartialOrd)] +#[derive(Debug, Eq, PartialEq, Hash)] #[repr(C)] pub struct HeaderWithLength { /// The fixed-sized data. @@ -253,6 +254,18 @@ impl From> for Arc<[T]> { pub(crate) type HeaderSliceWithLength = HeaderSlice, T>; +impl PartialOrd for HeaderSliceWithLength { + fn partial_cmp(&self, other: &Self) -> Option { + (&self.header.header, &self.slice).partial_cmp(&(&other.header.header, &other.slice)) + } +} + +impl Ord for HeaderSliceWithLength { + fn cmp(&self, other: &Self) -> Ordering { + (&self.header.header, &self.slice).cmp(&(&other.header.header, &other.slice)) + } +} + #[cfg(test)] mod tests { use alloc::boxed::Box; diff --git a/src/thin_arc.rs b/src/thin_arc.rs index e048468..19bb399 100644 --- a/src/thin_arc.rs +++ b/src/thin_arc.rs @@ -1,3 +1,4 @@ +use core::cmp::Ordering; use core::ffi::c_void; use core::fmt; use core::hash::{Hash, Hasher}; @@ -209,6 +210,20 @@ impl PartialEq for ThinArc { impl Eq for ThinArc {} +impl PartialOrd for ThinArc { + #[inline] + fn partial_cmp(&self, other: &ThinArc) -> Option { + ThinArc::with_arc(self, |a| ThinArc::with_arc(other, |b| a.partial_cmp(b))) + } +} + +impl Ord for ThinArc { + #[inline] + fn cmp(&self, other: &ThinArc) -> Ordering { + ThinArc::with_arc(self, |a| ThinArc::with_arc(other, |b| a.cmp(b))) + } +} + impl Hash for ThinArc { fn hash(&self, state: &mut HSR) { ThinArc::with_arc(self, |a| a.hash(state)) @@ -326,4 +341,92 @@ mod tests { } assert_eq!(canary.load(Acquire), 1); } + + #[test] + fn thin_eq_and_cmp() { + [ + [("*", &b"AB"[..]), ("*", &b"ab"[..])], + [("*", &b"AB"[..]), ("*", &b"a"[..])], + [("*", &b"A"[..]), ("*", &b"ab"[..])], + [("A", &b"*"[..]), ("a", &b"*"[..])], + [("a", &b"*"[..]), ("A", &b"*"[..])], + [("AB", &b"*"[..]), ("a", &b"*"[..])], + [("A", &b"*"[..]), ("ab", &b"*"[..])], + ] + .iter() + .for_each(|[lt @ (lh, ls), rt @ (rh, rs)]| { + let l = ThinArc::from_header_and_slice(lh, ls); + let r = ThinArc::from_header_and_slice(rh, rs); + + assert_eq!(l, l); + assert_eq!(r, r); + + assert_ne!(l, r); + assert_ne!(r, l); + + assert_eq!(l <= l, lt <= lt, "{lt:?} <= {lt:?}"); + assert_eq!(l >= l, lt >= lt, "{lt:?} >= {lt:?}"); + + assert_eq!(l < l, lt < lt, "{lt:?} < {lt:?}"); + assert_eq!(l > l, lt > lt, "{lt:?} > {lt:?}"); + + assert_eq!(r <= r, rt <= rt, "{rt:?} <= {rt:?}"); + assert_eq!(r >= r, rt >= rt, "{rt:?} >= {rt:?}"); + + assert_eq!(r < r, rt < rt, "{rt:?} < {rt:?}"); + assert_eq!(r > r, rt > rt, "{rt:?} > {rt:?}"); + + assert_eq!(l < r, lt < rt, "{lt:?} < {rt:?}"); + assert_eq!(r > l, rt > lt, "{rt:?} > {lt:?}"); + }) + } + + #[test] + fn thin_eq_and_partial_cmp() { + [ + [(0.0, &[0.0, 0.0][..]), (1.0, &[0.0, 0.0][..])], + [(1.0, &[0.0, 0.0][..]), (0.0, &[0.0, 0.0][..])], + [(0.0, &[0.0][..]), (0.0, &[0.0, 0.0][..])], + [(0.0, &[0.0, 0.0][..]), (0.0, &[0.0][..])], + [(0.0, &[1.0, 2.0][..]), (0.0, &[10.0, 20.0][..])], + ] + .iter() + .for_each(|[lt @ (lh, ls), rt @ (rh, rs)]| { + let l = ThinArc::from_header_and_slice(lh, ls); + let r = ThinArc::from_header_and_slice(rh, rs); + + assert_eq!(l, l); + assert_eq!(r, r); + + assert_ne!(l, r); + assert_ne!(r, l); + + assert_eq!(l <= l, lt <= lt, "{lt:?} <= {lt:?}"); + assert_eq!(l >= l, lt >= lt, "{lt:?} >= {lt:?}"); + + assert_eq!(l < l, lt < lt, "{lt:?} < {lt:?}"); + assert_eq!(l > l, lt > lt, "{lt:?} > {lt:?}"); + + assert_eq!(r <= r, rt <= rt, "{rt:?} <= {rt:?}"); + assert_eq!(r >= r, rt >= rt, "{rt:?} >= {rt:?}"); + + assert_eq!(r < r, rt < rt, "{rt:?} < {rt:?}"); + assert_eq!(r > r, rt > rt, "{rt:?} > {rt:?}"); + + assert_eq!(l < r, lt < rt, "{lt:?} < {rt:?}"); + assert_eq!(r > l, rt > lt, "{rt:?} > {lt:?}"); + }) + } + + #[allow(dead_code)] + const fn is_partial_ord() {} + + #[allow(dead_code)] + const fn is_ord() {} + + // compile-time check that PartialOrd/Ord is correctly derived + const _: () = is_partial_ord::>(); + const _: () = is_partial_ord::>(); + const _: () = is_partial_ord::>(); + const _: () = is_ord::>(); }