Skip to main content

triomphe/
offset_arc.rs

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