Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions src/arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T: ?Sized + PartialOrd>() {}

#[allow(dead_code)]
const fn is_ord<T: ?Sized + Ord>() {}

// compile-time check that PartialOrd/Ord is correctly derived
const _: () = is_partial_ord::<Arc<f64>>();
const _: () = is_ord::<Arc<u64>>();
}
17 changes: 15 additions & 2 deletions src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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<H, T: ?Sized> {
/// The fixed-sized data.
Expand Down Expand Up @@ -153,7 +154,7 @@ impl<H> Arc<HeaderSlice<H, str>> {

/// 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<H> {
/// The fixed-sized data.
Expand Down Expand Up @@ -253,6 +254,18 @@ impl<T> From<Vec<T>> for Arc<[T]> {

pub(crate) type HeaderSliceWithLength<H, T> = HeaderSlice<HeaderWithLength<H>, T>;

impl<H: PartialOrd, T: ?Sized + PartialOrd> PartialOrd for HeaderSliceWithLength<H, T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
(&self.header.header, &self.slice).partial_cmp(&(&other.header.header, &other.slice))
}
}

impl<H: Ord, T: ?Sized + Ord> Ord for HeaderSliceWithLength<H, T> {
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;
Expand Down
103 changes: 103 additions & 0 deletions src/thin_arc.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use core::cmp::Ordering;
use core::ffi::c_void;
use core::fmt;
use core::hash::{Hash, Hasher};
Expand Down Expand Up @@ -209,6 +210,20 @@ impl<H: PartialEq, T: PartialEq> PartialEq for ThinArc<H, T> {

impl<H: Eq, T: Eq> Eq for ThinArc<H, T> {}

impl<H: PartialOrd, T: PartialOrd> PartialOrd for ThinArc<H, T> {
#[inline]
fn partial_cmp(&self, other: &ThinArc<H, T>) -> Option<Ordering> {
ThinArc::with_arc(self, |a| ThinArc::with_arc(other, |b| a.partial_cmp(b)))
}
}

impl<H: Ord, T: Ord> Ord for ThinArc<H, T> {
#[inline]
fn cmp(&self, other: &ThinArc<H, T>) -> Ordering {
ThinArc::with_arc(self, |a| ThinArc::with_arc(other, |b| a.cmp(b)))
}
}

impl<H: Hash, T: Hash> Hash for ThinArc<H, T> {
fn hash<HSR: Hasher>(&self, state: &mut HSR) {
ThinArc::with_arc(self, |a| a.hash(state))
Expand Down Expand Up @@ -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<T: ?Sized + PartialOrd>() {}

#[allow(dead_code)]
const fn is_ord<T: ?Sized + Ord>() {}

// compile-time check that PartialOrd/Ord is correctly derived
const _: () = is_partial_ord::<ThinArc<f64, f64>>();
const _: () = is_partial_ord::<ThinArc<f64, u64>>();
const _: () = is_partial_ord::<ThinArc<u64, f64>>();
const _: () = is_ord::<ThinArc<u64, u64>>();
}