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