triomphe/
offset_arc.rs

1use core::fmt;
2use core::marker::PhantomData;
3use core::mem::ManuallyDrop;
4use core::ops::Deref;
5use core::ptr;
6
7use super::{Arc, ArcBorrow};
8
9/// An `Arc`, except it holds a pointer to the T instead of to the
10/// entire ArcInner.
11///
12/// An `OffsetArc<T>` has the same layout and ABI as a non-null
13/// `const T*` in C, and may be used in FFI function signatures.
14///
15/// ```text
16///  Arc<T>    OffsetArc<T>
17///   |          |
18///   v          v
19///  ---------------------
20/// | RefCount | T (data) | [ArcInner<T>]
21///  ---------------------
22/// ```
23///
24/// This means that this is a direct pointer to
25/// its contained data (and can be read from by both C++ and Rust),
26/// but we can also convert it to a "regular" `Arc<T>` by removing the offset.
27///
28/// This is very useful if you have an Arc-containing struct shared between Rust and C++,
29/// and wish for C++ to be able to read the data behind the `Arc` without incurring
30/// an FFI call overhead.
31#[derive(Eq)]
32#[repr(transparent)]
33pub struct OffsetArc<T> {
34    pub(crate) ptr: ptr::NonNull<T>,
35    pub(crate) phantom: PhantomData<T>,
36}
37
38unsafe impl<T: Sync + Send> Send for OffsetArc<T> {}
39unsafe impl<T: Sync + Send> Sync for OffsetArc<T> {}
40
41impl<T> Deref for OffsetArc<T> {
42    type Target = T;
43    #[inline]
44    fn deref(&self) -> &Self::Target {
45        unsafe { &*self.ptr.as_ptr() }
46    }
47}
48
49impl<T> Clone for OffsetArc<T> {
50    #[inline]
51    fn clone(&self) -> Self {
52        Arc::into_raw_offset(self.clone_arc())
53    }
54}
55
56impl<T> Drop for OffsetArc<T> {
57    fn drop(&mut self) {
58        let _ = Arc::from_raw_offset(OffsetArc {
59            ptr: self.ptr,
60            phantom: PhantomData,
61        });
62    }
63}
64
65impl<T: fmt::Debug> fmt::Debug for OffsetArc<T> {
66    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67        fmt::Debug::fmt(&**self, f)
68    }
69}
70
71impl<T: PartialEq> PartialEq for OffsetArc<T> {
72    fn eq(&self, other: &OffsetArc<T>) -> bool {
73        *(*self) == *(*other)
74    }
75
76    #[allow(clippy::partialeq_ne_impl)]
77    fn ne(&self, other: &OffsetArc<T>) -> bool {
78        *(*self) != *(*other)
79    }
80}
81
82impl<T> OffsetArc<T> {
83    /// Temporarily converts |self| into a bonafide Arc and exposes it to the
84    /// provided callback. The refcount is not modified.
85    #[inline]
86    pub fn with_arc<F, U>(&self, f: F) -> U
87    where
88        F: FnOnce(&Arc<T>) -> U,
89    {
90        // Synthesize transient Arc, which never touches the refcount of the ArcInner.
91        let transient = unsafe { ManuallyDrop::new(Arc::from_raw(self.ptr.as_ptr())) };
92
93        // Expose the transient Arc to the callback, which may clone it if it wants
94        // and forward the result to the user
95        f(&transient)
96    }
97
98    /// If uniquely owned, provide a mutable reference
99    /// Else create a copy, and mutate that
100    ///
101    /// This is functionally the same thing as `Arc::make_mut`
102    #[inline]
103    pub fn make_mut(&mut self) -> &mut T
104    where
105        T: Clone,
106    {
107        unsafe {
108            // extract the OffsetArc as an owned variable. This does not modify
109            // the refcount and we should be careful to not drop `this`
110            let this = ptr::read(self);
111            // treat it as a real Arc, but wrapped in a ManuallyDrop
112            // in case `Arc::make_mut()` panics in the clone impl
113            let mut arc = ManuallyDrop::new(Arc::from_raw_offset(this));
114            // obtain the mutable reference. Cast away the lifetime since
115            // we have the right lifetime bounds in the parameters.
116            // This may mutate `arc`.
117            let ret = Arc::make_mut(&mut *arc) as *mut _;
118            // Store the possibly-mutated arc back inside, after converting
119            // it to a OffsetArc again. Release the ManuallyDrop.
120            // This also does not modify the refcount or call drop on self
121            ptr::write(self, Arc::into_raw_offset(ManuallyDrop::into_inner(arc)));
122            &mut *ret
123        }
124    }
125
126    /// Clone it as an `Arc`
127    #[inline]
128    pub fn clone_arc(&self) -> Arc<T> {
129        OffsetArc::with_arc(self, |a| a.clone())
130    }
131
132    /// Produce a pointer to the data that can be converted back
133    /// to an `Arc`
134    #[inline]
135    pub fn borrow_arc(&self) -> ArcBorrow<'_, T> {
136        ArcBorrow(self.ptr, PhantomData)
137    }
138
139    /// The reference count of this `Arc`.
140    ///
141    /// The number does not include borrowed pointers,
142    /// or temporary `Arc` pointers created with functions like
143    /// [`ArcBorrow::with_arc`].
144    ///
145    /// The function is called `strong_count` to mirror `std::sync::Arc::strong_count`,
146    /// however `triomphe::Arc` does not support weak references.
147    #[inline]
148    pub fn strong_count(this: &Self) -> usize {
149        Self::with_arc(this, |arc| Arc::strong_count(arc))
150    }
151}