Skip to main content

triomphe/
thin_arc.rs

1use core::cmp::Ordering;
2use core::ffi::c_void;
3use core::fmt;
4use core::hash::{Hash, Hasher};
5use core::iter::{ExactSizeIterator, Iterator};
6use core::marker::PhantomData;
7use core::mem::ManuallyDrop;
8use core::ops::Deref;
9use core::panic::{RefUnwindSafe, UnwindSafe};
10use core::ptr;
11
12use super::{Arc, ArcInner, HeaderSlice, HeaderSliceWithLengthProtected, HeaderWithLength};
13use crate::header::HeaderSliceWithLengthUnchecked;
14use crate::{mdref, AllocError};
15
16/// A "thin" `Arc` containing dynamically sized data
17///
18/// This is functionally equivalent to `Arc<(H, [T])>`
19///
20/// When you create an `Arc` containing a dynamically sized type
21/// like `HeaderSlice<H, [T]>`, the `Arc` is represented on the stack
22/// as a "fat pointer", where the length of the slice is stored
23/// alongside the `Arc`'s pointer. In some situations you may wish to
24/// have a thin pointer instead, perhaps for FFI compatibility
25/// or space efficiency.
26///
27/// Note that we use `[T; 0]` in order to have the right alignment for `T`.
28///
29/// `ThinArc` solves this by storing the length in the allocation itself,
30/// via `HeaderSliceWithLengthProtected`.
31#[repr(transparent)]
32pub struct ThinArc<H, T> {
33    // We can pointer-cast between this target type
34    // of `ArcInner<HeaderSlice<HeaderWithLength<H>, [T; 0]>`
35    // and the types
36    // `ArcInner<HeaderSliceWithLengthProtected<H, T>>` and
37    // `ArcInner<HeaderSliceWithLengthUnchecked<H, T>>` (= `ArcInner<HeaderSlice<HeaderWithLength<H>, [T]>>`).
38    // [By adding appropriate length metadata to the pointer.]
39    // All types involved are #[repr(C)] or #[repr(transparent)], to ensure the safety of such casts
40    // (in particular `HeaderSlice`, `HeaderWithLength`, `HeaderSliceWithLengthProtected`).
41    //
42    // The safe API of `ThinArc` ensures that the length in the `HeaderWithLength`
43    // corretcly set - or verified - upon creation of a `ThinArc` and can't be modified
44    // to fall out of sync with the true slice length for this value & allocation.
45    ptr: ptr::NonNull<ArcInner<HeaderSlice<HeaderWithLength<H>, [T; 0]>>>,
46    phantom: PhantomData<(H, T)>,
47}
48
49unsafe impl<H: Sync + Send, T: Sync + Send> Send for ThinArc<H, T> {}
50unsafe impl<H: Sync + Send, T: Sync + Send> Sync for ThinArc<H, T> {}
51
52impl<H: RefUnwindSafe, T: RefUnwindSafe> UnwindSafe for ThinArc<H, T> {}
53
54// Synthesize a fat pointer from a thin pointer.
55//
56// See the comment around the analogous operation in from_header_and_iter.
57#[inline]
58const fn thin_to_thick<H, T>(
59    arc: &ThinArc<H, T>,
60) -> *mut ArcInner<HeaderSliceWithLengthProtected<H, T>> {
61    let thin = arc.ptr.as_ptr();
62    let len = unsafe { (*thin).data.header.length };
63    let fake_slice = ptr::slice_from_raw_parts_mut(thin as *mut T, len);
64
65    fake_slice as *mut ArcInner<HeaderSliceWithLengthProtected<H, T>>
66}
67
68impl<H, T> ThinArc<H, T> {
69    /// Temporarily converts |self| into a bonafide Arc and exposes it to the
70    /// provided callback. The refcount is not modified.
71    #[inline]
72    pub fn with_arc<F, U>(&self, f: F) -> U
73    where
74        F: FnOnce(&Arc<HeaderSliceWithLengthUnchecked<H, T>>) -> U,
75    {
76        // Synthesize transient Arc, which never touches the refcount of the ArcInner.
77        let transient = ManuallyDrop::new(Arc::from_protected(unsafe {
78            Arc::from_raw_inner(thin_to_thick(self))
79        }));
80
81        // Expose the transient Arc to the callback, which may clone it if it wants
82        // and forward the result to the user
83        f(&transient)
84    }
85
86    /// Temporarily converts |self| into a bonafide Arc and exposes it to the
87    /// provided callback. The refcount is not modified.
88    #[inline]
89    fn with_protected_arc<F, U>(&self, f: F) -> U
90    where
91        F: FnOnce(&Arc<HeaderSliceWithLengthProtected<H, T>>) -> U,
92    {
93        // Synthesize transient Arc, which never touches the refcount of the ArcInner.
94        let transient = ManuallyDrop::new(unsafe { Arc::from_raw_inner(thin_to_thick(self)) });
95
96        // Expose the transient Arc to the callback, which may clone it if it wants
97        // and forward the result to the user
98        f(&transient)
99    }
100
101    /// Temporarily converts |self| into a bonafide Arc and exposes it to the
102    /// provided callback. The refcount is not modified.
103    #[inline]
104    pub fn with_arc_mut<F, U>(&mut self, f: F) -> U
105    where
106        F: FnOnce(&mut Arc<HeaderSliceWithLengthProtected<H, T>>) -> U,
107    {
108        // It is possible for the user to replace the Arc entirely here. If so, we need to update the ThinArc as well
109        // whenever this method exits. We do this with a drop guard to handle the panicking case
110        struct DropGuard<'a, H, T> {
111            transient: ManuallyDrop<Arc<HeaderSliceWithLengthProtected<H, T>>>,
112            this: &'a mut ThinArc<H, T>,
113        }
114
115        impl<'a, H, T> Drop for DropGuard<'a, H, T> {
116            fn drop(&mut self) {
117                // This guard is only dropped when the same debug_assert already succeeded
118                // or while panicking. This has the effect that, if the debug_assert fails, we abort!
119                // This should never fail, unless a user used `transmute` to violate the invariants of
120                // `HeaderSliceWithLengthProtected`.
121                // In this case, there is no sound fallback other than aborting.
122                debug_assert_eq!(
123                    self.transient.length(),
124                    self.transient.slice().len(),
125                    "Length needs to be correct for ThinArc to work"
126                );
127                // Safety: We're still in the realm of Protected types so this cast is safe
128                self.this.ptr = self.transient.p.cast();
129            }
130        }
131
132        // Synthesize transient Arc, which never touches the refcount of the ArcInner.
133        let transient = ManuallyDrop::new(unsafe { Arc::from_raw_inner(thin_to_thick(self)) });
134
135        let mut guard = DropGuard {
136            transient,
137            this: self,
138        };
139
140        // Expose the transient Arc to the callback, which may clone it if it wants
141        // and forward the result to the user
142        let ret = f(&mut guard.transient);
143
144        // deliberately checked both here AND in the `DropGuard`
145        debug_assert_eq!(
146            guard.transient.length(),
147            guard.transient.slice().len(),
148            "Length needs to be correct for ThinArc to work"
149        );
150
151        ret
152    }
153
154    /// Creates a `ThinArc` for a HeaderSlice using the given header struct and
155    /// iterator to generate the slice.
156    pub fn from_header_and_iter<I>(header: H, items: I) -> Self
157    where
158        I: Iterator<Item = T> + ExactSizeIterator,
159    {
160        let header = HeaderWithLength::new(header, items.len());
161        Arc::into_thin(Arc::from_header_and_iter(header, items))
162    }
163
164    /// Fallible version of [`ThinArc::from_header_and_iter`].
165    ///
166    /// Returns `Err(AllocError)` instead of aborting on allocation failure.
167    pub fn try_from_header_and_iter<I>(header: H, items: I) -> Result<Self, AllocError>
168    where
169        I: Iterator<Item = T> + ExactSizeIterator,
170    {
171        let header = HeaderWithLength::new(header, items.len());
172        Ok(Arc::into_thin(Arc::try_from_header_and_iter(
173            header, items,
174        )?))
175    }
176
177    /// Creates a `ThinArc` for a HeaderSlice using the given header struct and
178    /// a slice to copy.
179    pub fn from_header_and_slice(header: H, items: &[T]) -> Self
180    where
181        T: Copy,
182    {
183        let header = HeaderWithLength::new(header, items.len());
184        Arc::into_thin(Arc::from_header_and_slice(header, items))
185    }
186
187    /// Fallible version of [`ThinArc::from_header_and_slice`].
188    ///
189    /// Returns `Err(AllocError)` instead of aborting on allocation failure.
190    pub fn try_from_header_and_slice(header: H, items: &[T]) -> Result<Self, AllocError>
191    where
192        T: Copy,
193    {
194        let header = HeaderWithLength::new(header, items.len());
195        Ok(Arc::into_thin(Arc::try_from_header_and_slice(
196            header, items,
197        )?))
198    }
199
200    /// Returns the address on the heap of the ThinArc itself -- not the T
201    /// within it -- for memory reporting.
202    #[inline]
203    pub const fn ptr(&self) -> *const c_void {
204        self.ptr.cast().as_ptr()
205    }
206
207    /// Returns the address on the heap of the Arc itself -- not the T within it -- for memory
208    /// reporting.
209    #[inline]
210    pub const fn heap_ptr(&self) -> *const c_void {
211        self.ptr()
212    }
213
214    /// # Safety
215    ///
216    /// Constructs an ThinArc from a raw pointer.
217    ///
218    /// The raw pointer must have been previously returned by a call to
219    /// ThinArc::into_raw.
220    ///
221    /// The user of from_raw has to make sure a specific value of T is only dropped once.
222    ///
223    /// This function is unsafe because improper use may lead to memory unsafety,
224    /// even if the returned ThinArc is never accessed.
225    #[inline]
226    pub const unsafe fn from_raw(ptr: *const c_void) -> Self {
227        Self {
228            ptr: ptr::NonNull::new_unchecked(ptr as *mut c_void).cast(),
229            phantom: PhantomData,
230        }
231    }
232
233    /// Consume ThinArc and returned the wrapped pointer.
234    #[inline]
235    pub const fn into_raw(self) -> *const c_void {
236        let this = ManuallyDrop::new(self);
237        mdref(&this).ptr()
238    }
239
240    /// Provides a raw pointer to the data.
241    /// The counts are not affected in any way and the ThinArc is not consumed.
242    /// The pointer is valid for as long as there are strong counts in the ThinArc.
243    #[inline]
244    pub const fn as_ptr(&self) -> *const c_void {
245        self.ptr()
246    }
247
248    /// The reference count of this `Arc`.
249    ///
250    /// The number does not include borrowed pointers,
251    /// or temporary `Arc` pointers created with functions like
252    /// [`ArcBorrow::with_arc`](crate::ArcBorrow::with_arc).
253    ///
254    /// The function is called `strong_count` to mirror `std::sync::Arc::strong_count`,
255    /// however `triomphe::Arc` does not support weak references.
256    #[inline]
257    pub fn strong_count(this: &Self) -> usize {
258        Self::with_arc(this, Arc::strong_count)
259    }
260}
261
262impl<H, T> Deref for ThinArc<H, T> {
263    type Target = HeaderSliceWithLengthUnchecked<H, T>;
264
265    #[inline]
266    fn deref(&self) -> &Self::Target {
267        unsafe { (*thin_to_thick(self)).data.inner() }
268    }
269}
270
271impl<H, T> Clone for ThinArc<H, T> {
272    #[inline]
273    fn clone(&self) -> Self {
274        ThinArc::with_protected_arc(self, |a| Arc::protected_into_thin(a.clone()))
275    }
276}
277
278impl<H, T> Drop for ThinArc<H, T> {
279    #[inline]
280    fn drop(&mut self) {
281        let _ = Arc::protected_from_thin(ThinArc {
282            ptr: self.ptr,
283            phantom: PhantomData,
284        });
285    }
286}
287
288impl<H, T> Arc<HeaderSliceWithLengthUnchecked<H, T>> {
289    /// Converts an `Arc` into a `ThinArc`. This consumes the `Arc`, so the refcount
290    /// is not modified.
291    ///
292    /// # Safety
293    /// Assumes that the header length matches the slice length.
294    #[inline]
295    unsafe fn into_thin_unchecked(a: Self) -> ThinArc<H, T> {
296        // Safety: invariant bubbled up
297        let this_protected: Arc<HeaderSliceWithLengthProtected<H, T>> =
298            unsafe { Arc::from_unprotected_unchecked(a) };
299
300        Arc::protected_into_thin(this_protected)
301    }
302
303    /// Converts an `Arc` into a `ThinArc`. This consumes the `Arc`, so the refcount
304    /// is not modified.
305    #[inline]
306    pub fn into_thin(a: Self) -> ThinArc<H, T> {
307        assert_eq!(
308            a.header.length,
309            a.slice.len(),
310            "Length needs to be correct for ThinArc to work"
311        );
312        // Safety: invariant checked in assertion above
313        unsafe { Self::into_thin_unchecked(a) }
314    }
315
316    /// Converts a `ThinArc` into an `Arc`. This consumes the `ThinArc`, so the refcount
317    /// is not modified.
318    #[inline]
319    pub const fn from_thin(a: ThinArc<H, T>) -> Self {
320        Self::from_protected(Arc::<HeaderSliceWithLengthProtected<H, T>>::protected_from_thin(a))
321    }
322
323    /// Converts an `Arc` into a `ThinArc`. This consumes the `Arc`, so the refcount
324    /// is not modified.
325    #[inline]
326    const fn from_protected(a: Arc<HeaderSliceWithLengthProtected<H, T>>) -> Self {
327        // Safety: HeaderSliceWithLengthProtected and HeaderSliceWithLengthUnchecked have the same layout
328        // The whole `Arc` should also be layout compatible (as a transparent wrapper around `NonNull` pointers with the same
329        // metadata type) but we still conservatively avoid a direct transmute here and use a pointer-cast instead.
330        unsafe { Arc::from_raw_inner(Arc::into_raw_inner(a) as _) }
331    }
332}
333
334impl<H, T> Arc<HeaderSliceWithLengthProtected<H, T>> {
335    /// Converts an `Arc` into a `ThinArc`. This consumes the `Arc`, so the refcount
336    /// is not modified.
337    #[inline]
338    pub const fn protected_into_thin(a: Self) -> ThinArc<H, T> {
339        debug_assert!(
340            a.inner().data.length() == a.inner().data.slice().len(),
341            "Length needs to be correct for ThinArc to work"
342        );
343
344        let fat_ptr: *mut ArcInner<HeaderSliceWithLengthProtected<H, T>> = Arc::into_raw_inner(a);
345        // Safety: The pointer comes from a valid Arc, and HeaderSliceWithLengthProtected has the correct length invariant
346        let thin_ptr: *mut ArcInner<HeaderSlice<HeaderWithLength<H>, [T; 0]>> = fat_ptr.cast();
347        ThinArc {
348            ptr: unsafe { ptr::NonNull::new_unchecked(thin_ptr) },
349            phantom: PhantomData,
350        }
351    }
352
353    /// Converts a `ThinArc` into an `Arc`. This consumes the `ThinArc`, so the refcount
354    /// is not modified.
355    #[inline]
356    pub const fn protected_from_thin(a: ThinArc<H, T>) -> Self {
357        let a = ManuallyDrop::new(a);
358        let ptr = thin_to_thick(mdref(&a));
359        unsafe { Arc::from_raw_inner(ptr) }
360    }
361
362    /// Obtains a HeaderSliceWithLengthProtected from an unchecked HeaderSliceWithLengthUnchecked, wrapped in an Arc
363    ///
364    /// # Safety
365    /// Assumes that the header length matches the slice length.
366    #[inline]
367    const unsafe fn from_unprotected_unchecked(
368        a: Arc<HeaderSliceWithLengthUnchecked<H, T>>,
369    ) -> Self {
370        // Safety: HeaderSliceWithLengthProtected and HeaderSliceWithLengthUnchecked have the same layout
371        // and the safety invariant on HeaderSliceWithLengthProtected.inner is bubbled up
372        // The whole `Arc` should also be layout compatible (as a transparent wrapper around `NonNull` pointers with the same
373        // metadata type) but we still conservatively avoid a direct transmute here and use a pointer-cast instead.
374        unsafe { Arc::from_raw_inner(Arc::into_raw_inner(a) as _) }
375    }
376}
377
378impl<H: PartialEq, T: PartialEq> PartialEq for ThinArc<H, T> {
379    #[inline]
380    fn eq(&self, other: &ThinArc<H, T>) -> bool {
381        ThinArc::with_arc(self, |a| ThinArc::with_arc(other, |b| *a == *b))
382    }
383}
384
385impl<H: Eq, T: Eq> Eq for ThinArc<H, T> {}
386
387impl<H: PartialOrd, T: PartialOrd> PartialOrd for ThinArc<H, T> {
388    #[inline]
389    fn partial_cmp(&self, other: &ThinArc<H, T>) -> Option<Ordering> {
390        ThinArc::with_arc(self, |a| ThinArc::with_arc(other, |b| a.partial_cmp(b)))
391    }
392}
393
394impl<H: Ord, T: Ord> Ord for ThinArc<H, T> {
395    #[inline]
396    fn cmp(&self, other: &ThinArc<H, T>) -> Ordering {
397        ThinArc::with_arc(self, |a| ThinArc::with_arc(other, |b| a.cmp(b)))
398    }
399}
400
401impl<H: Hash, T: Hash> Hash for ThinArc<H, T> {
402    fn hash<HSR: Hasher>(&self, state: &mut HSR) {
403        ThinArc::with_arc(self, |a| a.hash(state))
404    }
405}
406
407impl<H: fmt::Debug, T: fmt::Debug> fmt::Debug for ThinArc<H, T> {
408    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
409        fmt::Debug::fmt(&**self, f)
410    }
411}
412
413impl<H, T> fmt::Pointer for ThinArc<H, T> {
414    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
415        fmt::Pointer::fmt(&self.ptr(), f)
416    }
417}
418
419#[cfg(test)]
420mod tests {
421    use crate::{Arc, HeaderWithLength, ThinArc};
422    use alloc::vec;
423    use core::clone::Clone;
424    use core::ops::Drop;
425    use core::sync::atomic;
426    use core::sync::atomic::Ordering::{Acquire, SeqCst};
427
428    #[derive(PartialEq)]
429    struct Canary(*mut atomic::AtomicUsize);
430
431    impl Drop for Canary {
432        fn drop(&mut self) {
433            unsafe {
434                (*self.0).fetch_add(1, SeqCst);
435            }
436        }
437    }
438
439    #[test]
440    fn empty_thin() {
441        let header = HeaderWithLength::new(100u32, 0);
442        let x = Arc::from_header_and_iter(header, core::iter::empty::<i32>());
443        let y = Arc::into_thin(x.clone());
444        assert_eq!(y.header.header, 100);
445        assert!(y.slice.is_empty());
446        assert_eq!(x.header.header, 100);
447        assert!(x.slice.is_empty());
448    }
449
450    #[test]
451    fn try_from_header_and_iter_thin() {
452        let a = ThinArc::try_from_header_and_iter(42u32, vec![1u16, 2, 3].into_iter()).unwrap();
453        assert_eq!(a.header.header, 42);
454        assert_eq!(&a.slice, [1, 2, 3]);
455    }
456
457    #[test]
458    fn try_from_header_and_slice_thin() {
459        let a = ThinArc::try_from_header_and_slice(42u32, &[1u16, 2, 3]).unwrap();
460        assert_eq!(a.header.header, 42);
461        assert_eq!(&a.slice, [1, 2, 3]);
462    }
463
464    #[test]
465    fn thin_assert_padding() {
466        #[derive(Clone, Default)]
467        #[repr(C)]
468        struct Padded {
469            i: u16,
470        }
471
472        // The header will have more alignment than `Padded`
473        let header = HeaderWithLength::new(0i32, 2);
474        let items = vec![Padded { i: 0xdead }, Padded { i: 0xbeef }];
475        let a = ThinArc::from_header_and_iter(header, items.into_iter());
476        assert_eq!(a.slice.len(), 2);
477        assert_eq!(a.slice[0].i, 0xdead);
478        assert_eq!(a.slice[1].i, 0xbeef);
479    }
480
481    #[test]
482    #[allow(clippy::redundant_clone, clippy::eq_op)]
483    fn slices_and_thin() {
484        let mut canary = atomic::AtomicUsize::new(0);
485        let c = Canary(&mut canary as *mut atomic::AtomicUsize);
486        let v = vec![5, 6];
487        let header = HeaderWithLength::new(c, v.len());
488        {
489            let x = Arc::into_thin(Arc::from_header_and_slice(header, &v));
490            let y = ThinArc::with_arc(&x, |q| q.clone());
491            let _ = y.clone();
492            let _ = x == x;
493            Arc::from_thin(x.clone());
494        }
495        assert_eq!(canary.load(Acquire), 1);
496    }
497
498    #[test]
499    #[allow(clippy::redundant_clone, clippy::eq_op)]
500    fn iter_and_thin() {
501        let mut canary = atomic::AtomicUsize::new(0);
502        let c = Canary(&mut canary as *mut atomic::AtomicUsize);
503        let v = vec![5, 6];
504        let header = HeaderWithLength::new(c, v.len());
505        {
506            let x = Arc::into_thin(Arc::from_header_and_iter(header, v.into_iter()));
507            let y = ThinArc::with_arc(&x, |q| q.clone());
508            let _ = y.clone();
509            let _ = x == x;
510            Arc::from_thin(x.clone());
511        }
512        assert_eq!(canary.load(Acquire), 1);
513    }
514
515    #[test]
516    fn into_raw_and_from_raw() {
517        let mut canary = atomic::AtomicUsize::new(0);
518        let c = Canary(&mut canary as *mut atomic::AtomicUsize);
519        let v = vec![5, 6];
520        let header = HeaderWithLength::new(c, v.len());
521        {
522            type ThinArcCanary = ThinArc<Canary, u32>;
523            let x: ThinArcCanary = Arc::into_thin(Arc::from_header_and_iter(header, v.into_iter()));
524            let ptr = x.as_ptr();
525
526            assert_eq!(x.into_raw(), ptr);
527
528            let _x = unsafe { ThinArcCanary::from_raw(ptr) };
529        }
530        assert_eq!(canary.load(Acquire), 1);
531    }
532
533    #[test]
534    fn thin_eq_and_cmp() {
535        [
536            [("*", &b"AB"[..]), ("*", &b"ab"[..])],
537            [("*", &b"AB"[..]), ("*", &b"a"[..])],
538            [("*", &b"A"[..]), ("*", &b"ab"[..])],
539            [("A", &b"*"[..]), ("a", &b"*"[..])],
540            [("a", &b"*"[..]), ("A", &b"*"[..])],
541            [("AB", &b"*"[..]), ("a", &b"*"[..])],
542            [("A", &b"*"[..]), ("ab", &b"*"[..])],
543        ]
544        .iter()
545        .for_each(|[lt @ (lh, ls), rt @ (rh, rs)]| {
546            let l = ThinArc::from_header_and_slice(lh, ls);
547            let r = ThinArc::from_header_and_slice(rh, rs);
548
549            assert_eq!(l, l);
550            assert_eq!(r, r);
551
552            assert_ne!(l, r);
553            assert_ne!(r, l);
554
555            assert_eq!(l <= l, lt <= lt, "{lt:?} <= {lt:?}");
556            assert_eq!(l >= l, lt >= lt, "{lt:?} >= {lt:?}");
557
558            assert_eq!(l < l, lt < lt, "{lt:?} < {lt:?}");
559            assert_eq!(l > l, lt > lt, "{lt:?} > {lt:?}");
560
561            assert_eq!(r <= r, rt <= rt, "{rt:?} <= {rt:?}");
562            assert_eq!(r >= r, rt >= rt, "{rt:?} >= {rt:?}");
563
564            assert_eq!(r < r, rt < rt, "{rt:?} < {rt:?}");
565            assert_eq!(r > r, rt > rt, "{rt:?} > {rt:?}");
566
567            assert_eq!(l < r, lt < rt, "{lt:?} < {rt:?}");
568            assert_eq!(r > l, rt > lt, "{rt:?} > {lt:?}");
569        })
570    }
571
572    #[test]
573    fn thin_eq_and_partial_cmp() {
574        [
575            [(0.0, &[0.0, 0.0][..]), (1.0, &[0.0, 0.0][..])],
576            [(1.0, &[0.0, 0.0][..]), (0.0, &[0.0, 0.0][..])],
577            [(0.0, &[0.0][..]), (0.0, &[0.0, 0.0][..])],
578            [(0.0, &[0.0, 0.0][..]), (0.0, &[0.0][..])],
579            [(0.0, &[1.0, 2.0][..]), (0.0, &[10.0, 20.0][..])],
580        ]
581        .iter()
582        .for_each(|[lt @ (lh, ls), rt @ (rh, rs)]| {
583            let l = ThinArc::from_header_and_slice(lh, ls);
584            let r = ThinArc::from_header_and_slice(rh, rs);
585
586            assert_eq!(l, l);
587            assert_eq!(r, r);
588
589            assert_ne!(l, r);
590            assert_ne!(r, l);
591
592            assert_eq!(l <= l, lt <= lt, "{lt:?} <= {lt:?}");
593            assert_eq!(l >= l, lt >= lt, "{lt:?} >= {lt:?}");
594
595            assert_eq!(l < l, lt < lt, "{lt:?} < {lt:?}");
596            assert_eq!(l > l, lt > lt, "{lt:?} > {lt:?}");
597
598            assert_eq!(r <= r, rt <= rt, "{rt:?} <= {rt:?}");
599            assert_eq!(r >= r, rt >= rt, "{rt:?} >= {rt:?}");
600
601            assert_eq!(r < r, rt < rt, "{rt:?} < {rt:?}");
602            assert_eq!(r > r, rt > rt, "{rt:?} > {rt:?}");
603
604            assert_eq!(l < r, lt < rt, "{lt:?} < {rt:?}");
605            assert_eq!(r > l, rt > lt, "{rt:?} > {lt:?}");
606        })
607    }
608
609    #[test]
610    fn with_arc_mut() {
611        let mut arc: ThinArc<u8, u16> = ThinArc::from_header_and_slice(1u8, &[1, 2, 3]);
612        arc.with_arc_mut(|arc| Arc::get_mut(arc).unwrap().slice_mut().fill(2));
613        arc.with_arc_mut(|arc| assert!(Arc::get_unique(arc).is_some()));
614        arc.with_arc(|arc| assert!(Arc::is_unique(arc)));
615        // Using clone to that the layout generated in new_uninit_slice is compatible
616        // with ArcInner.
617        let arcs = [
618            arc.clone(),
619            arc.clone(),
620            arc.clone(),
621            arc.clone(),
622            arc.clone(),
623        ];
624        arc.with_arc(|arc| assert_eq!(6, Arc::count(arc)));
625
626        // If the layout is not compatible, then the data might be corrupted.
627        assert_eq!(arc.header.header, 1);
628        assert_eq!(&arc.slice, [2, 2, 2]);
629
630        // Drop the arcs and check the count and the content to
631        // make sure it isn't corrupted.
632        drop(arcs);
633        arc.with_arc_mut(|arc| assert!(Arc::get_unique(arc).is_some()));
634        arc.with_arc(|arc| assert!(Arc::is_unique(arc)));
635        assert_eq!(arc.header.header, 1);
636        assert_eq!(&arc.slice, [2, 2, 2]);
637    }
638
639    #[allow(dead_code)]
640    const fn is_partial_ord<T: ?Sized + PartialOrd>() {}
641
642    #[allow(dead_code)]
643    const fn is_ord<T: ?Sized + Ord>() {}
644
645    // compile-time check that PartialOrd/Ord is correctly derived
646    const _: () = is_partial_ord::<ThinArc<f64, f64>>();
647    const _: () = is_partial_ord::<ThinArc<f64, u64>>();
648    const _: () = is_partial_ord::<ThinArc<u64, f64>>();
649    const _: () = is_ord::<ThinArc<u64, u64>>();
650}