thin_cell/
lib.rs

1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3#![deny(rustdoc::broken_intra_doc_links)]
4
5use std::{
6    cell::{Cell, UnsafeCell},
7    marker::PhantomData,
8    mem::{ManuallyDrop, size_of},
9    ops::{Deref, DerefMut},
10    ptr::NonNull,
11};
12
13mod state;
14use state::State;
15
16mod fat_ptr;
17use fat_ptr::FatPtr;
18
19/// The inner allocation of `ThinCell`
20///
21/// This should not be used except in unsize coercion solely as a type.
22#[repr(C)]
23pub struct Inner<T: ?Sized> {
24    // metadata MUST be at offset 0 so that `*mut Inner<T>` is also a valid `*mut usize` points
25    // to the metadata
26    metadata: usize,
27    state: Cell<State>,
28    data: UnsafeCell<T>,
29}
30
31/// A compact (`1-usize`), single-threaded smart pointer combining `Rc`
32/// and `borrow_mut`-only `RefCell`
33pub struct ThinCell<T: ?Sized> {
34    ptr: NonNull<()>,
35    _marker: PhantomData<Inner<T>>,
36}
37
38/// A mutable guard returned by [`ThinCell::borrow`]
39pub struct Ref<'a, T: ?Sized> {
40    value: &'a mut T,
41    state_cell: &'a Cell<State>,
42}
43
44impl<T> ThinCell<T> {
45    /// Creates a new `ThinCell` wrapping the given data.
46    pub fn new(data: T) -> Self {
47        let inner = Inner {
48            metadata: 0,
49            state: Cell::new(State::new()),
50            data: UnsafeCell::new(data),
51        };
52
53        let ptr = Box::into_raw(Box::new(inner));
54
55        ThinCell {
56            ptr: unsafe { NonNull::new_unchecked(ptr as _) },
57            _marker: PhantomData,
58        }
59    }
60
61    /// Consumes the `ThinCell`, returning the inner value if there are no
62    /// other owners and it is not currently borrowed.
63    pub fn try_unwrap(self) -> Result<T, Self> {
64        let inner = self.inner();
65        let s = inner.state.get();
66
67        if s.count() != 1 || s.is_borrowed() {
68            return Err(self);
69        }
70
71        // SAFETY: As tested above, there are no other owners and it is not borrowed
72        Ok(unsafe { self.unwrap_unchecked() })
73    }
74
75    /// Consumes the `ThinCell`, returning the inner value.
76    ///
77    /// # Safety
78    /// The caller must guarantee that there are no other owners and it is not
79    /// currently borrowed.
80    pub unsafe fn unwrap_unchecked(self) -> T {
81        let this = ManuallyDrop::new(self);
82        // SAFETY: guaranteed by caller to have unique ownership and is not borrowed
83        let inner = unsafe { Box::from_raw(this.inner_ptr() as *mut Inner<T>) };
84
85        inner.data.into_inner()
86    }
87}
88
89impl<T: ?Sized> ThinCell<T> {
90    const SIZED: bool = size_of::<*const Inner<T>>() == size_of::<usize>();
91
92    /// Creates a new `ThinCell<U>` from `data: U` and coerces it to
93    /// `ThinCell<T>`.
94    ///
95    /// # Safety
96    /// `coerce` function must ensure the returned pointer is:
97    /// - a valid unsizing of `T`, e.g., some `dyn Trait` with concrete type `U`
98    /// - with same address (bare data pointer without metadata) as input
99    pub unsafe fn new_unsize<U>(
100        data: U,
101        coerce: impl Fn(*const Inner<U>) -> *const Inner<T>,
102    ) -> Self {
103        let this = ThinCell::new(data);
104        // SAFETY: We're holding unique ownership and is not borrowed.
105        unsafe { this.unsize_unchecked(coerce) }
106    }
107
108    /// Reconstructs the raw pointer to the inner allocation.
109    fn inner_ptr(&self) -> *const Inner<T> {
110        unsafe {
111            let ptr = self.ptr.as_ptr();
112
113            if Self::SIZED {
114                // SIZED CASE: Cast pointer-to-pointer
115                // Doing this trick to workaround Rust not allowing `ptr as *const Inner<T>`
116                // due to `T` being `?Sized` directly even when we know it's `Sized`
117                let ptr_ref = &ptr as *const *mut () as *const *const Inner<T>;
118                *ptr_ref
119            } else {
120                // UNSIZED CASE: Read metadata
121                let metadata = *(ptr as *const usize);
122
123                // Miri will complain about this:
124                // - https://github.com/thepowersgang/stack_dst-rs/issues/14
125                // - https://github.com/uazu/stakker/blob/5821c30409c19ca9167808b669c928c94bc5f177/src/queue/flat.rs#L14-L17
126                // But this should be sound as per Rust's fat pointer and metadata construction
127                FatPtr { ptr, metadata }.into_ptr()
128            }
129        }
130    }
131
132    /// Returns a reference to the inner allocation.
133    fn inner(&self) -> &Inner<T> {
134        unsafe { &*self.inner_ptr() }
135    }
136
137    /// Deallocates the inner allocation.
138    ///
139    /// # Safety
140    /// `self` must be the last owner and it must not be used after this call.
141    unsafe fn drop_in_place(&mut self) {
142        drop(unsafe { Box::from_raw(self.inner_ptr() as *mut Inner<T>) })
143    }
144
145    /// Leaks the `ThinCell`, returning a raw pointer to the inner allocation.
146    ///
147    /// The returned pointer points to the inner allocation. To restore the
148    /// `ThinCell`, use [`ThinCell::from_raw`].
149    pub fn leak(self) -> *mut () {
150        let this = ManuallyDrop::new(self);
151        this.ptr.as_ptr()
152    }
153
154    /// Reconstructs a `ThinCell<T>` from a raw pointer.
155    ///
156    /// # Safety
157    /// The pointer must have been obtained from a previous call to
158    /// [`ThinCell::leak`], and the [`ThinCell`] must not have been dropped in
159    /// the meantime.
160    pub unsafe fn from_raw(ptr: *mut ()) -> Self {
161        ThinCell {
162            // SAFETY: caller guarantees `ptr` is valid
163            ptr: unsafe { NonNull::new_unchecked(ptr) },
164            _marker: PhantomData,
165        }
166    }
167
168    /// Returns the number of owners.
169    pub fn count(&self) -> usize {
170        self.inner().state.get().count()
171    }
172
173    /// Borrows the value mutably.
174    ///
175    /// Returns a [`Ref`] guard that provides mutable access to the inner value.
176    /// The borrow lasts until the guard is dropped.
177    ///
178    /// # Panics
179    ///
180    /// Panics if the value is already borrowed. For a non-panicking variant,
181    /// use [`try_borrow`](ThinCell::try_borrow).
182    ///
183    /// # Examples
184    ///
185    /// ```
186    /// # use thin_cell::ThinCell;
187    /// let cell = ThinCell::new(5);
188    ///
189    /// {
190    ///     let mut borrowed = cell.borrow();
191    ///     *borrowed = 10;
192    /// } // borrow is released here
193    ///
194    /// assert_eq!(*cell.borrow(), 10);
195    /// ```
196    pub fn borrow(&self) -> Ref<'_, T> {
197        let inner = self.inner();
198        inner.state.update(State::borrow);
199
200        // SAFETY: We have exclusive access via borrow flag
201        let value = unsafe { &mut *inner.data.get() };
202
203        Ref {
204            value,
205            state_cell: &inner.state,
206        }
207    }
208
209    /// Attempts to borrow the value mutably.
210    ///
211    /// Returns `Some(Ref)` if the value is not currently borrowed, or `None` if
212    /// it is already borrowed.
213    ///
214    /// This is the non-panicking variant of [`borrow`](ThinCell::borrow).
215    ///
216    /// # Examples
217    ///
218    /// ```
219    /// # use thin_cell::ThinCell;
220    /// let cell = ThinCell::new(5);
221    ///
222    /// let borrow1 = cell.borrow();
223    /// assert!(cell.try_borrow().is_none()); // Already borrowed
224    /// drop(borrow1);
225    /// assert!(cell.try_borrow().is_some()); // Now available
226    /// ```
227    pub fn try_borrow(&self) -> Option<Ref<'_, T>> {
228        let inner = self.inner();
229        let state = inner.state.get().try_borrow()?;
230        inner.state.set(state);
231
232        // SAFETY: We have exclusive access via borrow flag
233        let value = unsafe { &mut *inner.data.get() };
234
235        Some(Ref {
236            value,
237            state_cell: &inner.state,
238        })
239    }
240
241    /// Manually coerce to unsize without checks
242    ///
243    /// The returned `U` must be a valid unsizing of `T`, i.e., some `dyn
244    /// Trait` with concrete type `T`. `&U` must be a fat pointer. If not, this
245    /// will fail to compile.
246    ///
247    /// # Safety
248    ///
249    /// The `ThinCell` must:
250    /// - have unique ownership (count == 1)
251    /// - not be borrowed
252    ///
253    /// In particular, this is the exact state after [`ThinCell::new`].
254    ///
255    /// `coerce` function must ensure the returned pointer:
256    /// - is a valid unsizing of `T`, e.g., some `dyn Trait` with concrete type
257    ///   `U`
258    /// - consists the same address (bare data pointer without metadata) as the
259    ///   input
260    pub unsafe fn unsize_unchecked<U: ?Sized>(
261        self,
262        coerce: impl Fn(*const Inner<T>) -> *const Inner<U>,
263    ) -> ThinCell<U> {
264        let this = ManuallyDrop::new(self);
265
266        let old_ptr = this.inner_ptr();
267        let fat_ptr = coerce(old_ptr);
268
269        let FatPtr { ptr, metadata } = FatPtr::from_ptr::<Inner<U>>(fat_ptr);
270
271        // SAFETY: `Inner` is `repr(C)` and has `metadata` at offset 0
272        unsafe { *(old_ptr as *mut usize) = metadata };
273
274        ThinCell {
275            // SAFETY: `ptr` is valid as it comes from `self`
276            ptr: unsafe { NonNull::new_unchecked(ptr) },
277            _marker: PhantomData,
278        }
279    }
280
281    /// Manually coerce to unsize with some checks.
282    ///
283    /// # Safety
284    /// `coerce` function must ensure the returned pointer is:
285    /// - a valid unsizing of `T`, e.g., some `dyn Trait` with concrete type `U`
286    /// - with same address (bare data pointer without metadata) as input
287    ///
288    /// See [`ThinCell::unsize_unchecked`] for details.
289    pub unsafe fn unsize<U: ?Sized>(
290        self,
291        coerce: impl Fn(*const Inner<T>) -> *const Inner<U>,
292    ) -> ThinCell<U> {
293        let inner = self.inner();
294        let s = inner.state.get();
295
296        assert!(!s.is_shared(), "Cannot coerce shared `ThinCell`");
297        assert!(!s.is_borrowed(), "Cannot coerce borrowed `ThinCell`");
298
299        // SAFETY: As tested above, the `ThinCell` is:
300        // - not shared, and
301        // - not borrowed
302        // - validity of `coerce` is guaranteed by caller
303        unsafe { self.unsize_unchecked(coerce) }
304    }
305}
306
307impl<'a, T: ?Sized> Drop for Ref<'a, T> {
308    fn drop(&mut self) {
309        let current = self.state_cell.get();
310        self.state_cell.set(current.unborrow());
311    }
312}
313
314impl<'a, T: ?Sized> Deref for Ref<'a, T> {
315    type Target = T;
316
317    fn deref(&self) -> &T {
318        self.value
319    }
320}
321
322impl<'a, T: ?Sized> DerefMut for Ref<'a, T> {
323    fn deref_mut(&mut self) -> &mut T {
324        self.value
325    }
326}
327
328impl<T: ?Sized> Clone for ThinCell<T> {
329    fn clone(&self) -> Self {
330        unsafe {
331            let inner = &*self.inner_ptr();
332            let current = inner.state.get();
333
334            match current.inc() {
335                Some(new_state) => inner.state.set(new_state),
336                None => panic!("Reference count overflow"),
337            }
338
339            ThinCell {
340                ptr: self.ptr,
341                _marker: PhantomData,
342            }
343        }
344    }
345}
346
347impl<T: ?Sized> Drop for ThinCell<T> {
348    fn drop(&mut self) {
349        unsafe {
350            let ptr = self.inner_ptr();
351            // SAFETY: pointer returned by `inner_ptr` is valid
352            let inner = &*ptr;
353            let current = inner.state.get();
354
355            // If count is 1, we are the last owner
356            if current.count() == 1 {
357                debug_assert!(!current.is_borrowed(), "Dropping while borrowed");
358                self.drop_in_place();
359            } else {
360                // Not last owner, decrement
361                inner.state.set(current.dec());
362            }
363        }
364    }
365}