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    any::{Any, TypeId},
7    cell::{Cell, UnsafeCell},
8    fmt::{self, Debug, Display},
9    marker::PhantomData,
10    mem::{ManuallyDrop, size_of},
11    ops::{Deref, DerefMut},
12    ptr::NonNull,
13};
14
15mod state;
16use state::State;
17
18mod fat_ptr;
19use fat_ptr::FatPtr;
20
21/// The inner allocation of `ThinCell`
22///
23/// This should not be used except in unsize coercion solely as a type.
24#[repr(C)]
25pub struct Inner<T: ?Sized> {
26    // metadata MUST be at offset 0 so that `*mut Inner<T>` is also a valid `*mut usize` points
27    // to the metadata
28    metadata: usize,
29    state: Cell<State>,
30    data: UnsafeCell<T>,
31}
32
33/// A compact (`1-usize`), single-threaded smart pointer combining `Rc`
34/// and `borrow_mut`-only `RefCell`
35pub struct ThinCell<T: ?Sized> {
36    ptr: NonNull<()>,
37    _marker: PhantomData<Inner<T>>,
38}
39
40/// A mutable guard returned by [`ThinCell::borrow`]
41pub struct Ref<'a, T: ?Sized> {
42    value: &'a mut T,
43    state_cell: &'a Cell<State>,
44}
45
46impl<T> ThinCell<T> {
47    /// Creates a new `ThinCell` wrapping the given data.
48    pub fn new(data: T) -> Self {
49        let inner = Inner {
50            metadata: 0,
51            state: Cell::new(State::new()),
52            data: UnsafeCell::new(data),
53        };
54
55        let ptr = Box::into_raw(Box::new(inner));
56
57        ThinCell {
58            ptr: unsafe { NonNull::new_unchecked(ptr as _) },
59            _marker: PhantomData,
60        }
61    }
62
63    /// Consumes the `ThinCell` and try to get inner value.
64    ///
65    /// Returns the inner value in [`Ok`] if there are no other owners and it is
66    /// not currently borrowed, return `Err(self)` otherwise.
67    pub fn try_unwrap(self) -> Result<T, Self> {
68        let inner = self.inner();
69        let s = inner.state.get();
70
71        if s.count() != 1 || s.is_borrowed() {
72            return Err(self);
73        }
74
75        // SAFETY: As tested above, there are no other owners and it is not borrowed
76        Ok(unsafe { self.unwrap_unchecked() })
77    }
78
79    /// Consumes the `ThinCell`, returning the inner value.
80    ///
81    /// # Safety
82    /// The caller must guarantee that there are no other owners and it is not
83    /// currently borrowed.
84    pub unsafe fn unwrap_unchecked(self) -> T {
85        let this = ManuallyDrop::new(self);
86        // SAFETY: guaranteed by caller to have unique ownership and is not borrowed
87        let inner = unsafe { Box::from_raw(this.inner_ptr() as *mut Inner<T>) };
88
89        inner.data.into_inner()
90    }
91}
92
93impl<T: ?Sized> ThinCell<T> {
94    const SIZED: bool = size_of::<*const Inner<T>>() == size_of::<usize>();
95
96    /// Reconstructs the raw pointer to the inner allocation.
97    fn inner_ptr(&self) -> *const Inner<T> {
98        unsafe {
99            let ptr = self.ptr.as_ptr();
100
101            if Self::SIZED {
102                // SIZED CASE: Cast pointer-to-pointer
103                // Doing this trick to workaround Rust not allowing `ptr as *const Inner<T>`
104                // due to `T` being `?Sized` directly even when we know it's `Sized`
105                let ptr_ref = &ptr as *const *mut () as *const *const Inner<T>;
106                *ptr_ref
107            } else {
108                // UNSIZED CASE: Read metadata
109                let metadata = *(ptr as *const usize);
110
111                // Miri will complain about this:
112                // - https://github.com/thepowersgang/stack_dst-rs/issues/14
113                // - https://github.com/uazu/stakker/blob/5821c30409c19ca9167808b669c928c94bc5f177/src/queue/flat.rs#L14-L17
114                // But this should be sound as per Rust's fat pointer and metadata construction
115                FatPtr { ptr, metadata }.into_ptr()
116            }
117        }
118    }
119
120    /// Returns a reference to the inner allocation.
121    fn inner(&self) -> &Inner<T> {
122        unsafe { &*self.inner_ptr() }
123    }
124
125    /// Deallocates the inner allocation.
126    ///
127    /// # Safety
128    /// `self` must be the last owner and it must not be used after this call.
129    unsafe fn drop_in_place(&mut self) {
130        drop(unsafe { Box::from_raw(self.inner_ptr() as *mut Inner<T>) })
131    }
132
133    /// Leaks the `ThinCell`, returning a raw pointer to the inner allocation.
134    ///
135    /// The returned pointer points to the inner allocation. To restore the
136    /// `ThinCell`, use [`ThinCell::from_raw`].
137    pub fn leak(self) -> *mut () {
138        let this = ManuallyDrop::new(self);
139        this.ptr.as_ptr()
140    }
141
142    /// Reconstructs a `ThinCell<T>` from a raw pointer.
143    ///
144    /// # Safety
145    /// The pointer must have been obtained from a previous call to
146    /// [`ThinCell::leak`], and the [`ThinCell`] must not have been dropped in
147    /// the meantime.
148    pub unsafe fn from_raw(ptr: *mut ()) -> Self {
149        ThinCell {
150            // SAFETY: caller guarantees `ptr` is valid
151            ptr: unsafe { NonNull::new_unchecked(ptr) },
152            _marker: PhantomData,
153        }
154    }
155
156    /// Downcasts the `ThinCell<T>` to `ThinCell<U>`.
157    ///
158    /// # Safety
159    /// The caller must ensure that the inner value is actually of type `U`.
160    pub unsafe fn downcast_unchecked<U>(self) -> ThinCell<U> {
161        let this = ManuallyDrop::new(self);
162        ThinCell {
163            ptr: this.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    /// Get a mutable reference to the inner value without any checks.
242    ///
243    /// # Safety
244    /// The caller must guarantee that there are no other owners and it is not
245    /// currently borrowed.
246    pub unsafe fn borrow_unchecked(&mut self) -> &mut T {
247        let inner = self.inner();
248        unsafe { &mut *inner.data.get() }
249    }
250
251    /// Creates a new `ThinCell<U>` from `data: U` and coerces it to
252    /// `ThinCell<T>`.
253    ///
254    /// # Safety
255    /// `coerce` function must ensure the returned pointer is:
256    /// - a valid unsizing of `T`, e.g., some `dyn Trait` with concrete type `U`
257    /// - with same address (bare data pointer without metadata) as input
258    pub unsafe fn new_unsize<U>(
259        data: U,
260        coerce: impl Fn(*const Inner<U>) -> *const Inner<T>,
261    ) -> Self {
262        let this = ThinCell::new(data);
263        // SAFETY: We're holding unique ownership and is not borrowed.
264        unsafe { this.unsize_unchecked(coerce) }
265    }
266
267    /// Manually coerce to unsize with some checks.
268    ///
269    /// # Safety
270    /// `coerce` function must ensure the returned pointer is:
271    /// - a valid unsizing of `T`, e.g., some `dyn Trait` with concrete type `U`
272    /// - with same address (bare data pointer without metadata) as input
273    ///
274    /// See [`ThinCell::unsize_unchecked`] for details.
275    pub unsafe fn unsize<U: ?Sized>(
276        self,
277        coerce: impl Fn(*const Inner<T>) -> *const Inner<U>,
278    ) -> ThinCell<U> {
279        let inner = self.inner();
280        let s = inner.state.get();
281
282        assert!(!s.is_shared(), "Cannot coerce shared `ThinCell`");
283        assert!(!s.is_borrowed(), "Cannot coerce borrowed `ThinCell`");
284
285        // SAFETY: As tested above, the `ThinCell` is:
286        // - not shared, and
287        // - not borrowed
288        // - validity of `coerce` is guaranteed by caller
289        unsafe { self.unsize_unchecked(coerce) }
290    }
291
292    /// Manually coerce to unsize without checks
293    ///
294    /// The returned `U` must be a valid unsizing of `T`, i.e., some `dyn
295    /// Trait` with concrete type `T`. `&U` must be a fat pointer. If not, this
296    /// will fail to compile.
297    ///
298    /// # Safety
299    ///
300    /// The `ThinCell` must:
301    /// - have unique ownership (count == 1)
302    /// - not be borrowed
303    ///
304    /// In particular, this is the exact state after [`ThinCell::new`].
305    ///
306    /// `coerce` function must ensure the returned pointer:
307    /// - is a valid unsizing of `T`, e.g., some `dyn Trait` with concrete type
308    ///   `U`
309    /// - consists the same address (bare data pointer without metadata) as the
310    ///   input
311    pub unsafe fn unsize_unchecked<U: ?Sized>(
312        self,
313        coerce: impl Fn(*const Inner<T>) -> *const Inner<U>,
314    ) -> ThinCell<U> {
315        let this = ManuallyDrop::new(self);
316
317        let old_ptr = this.inner_ptr();
318        let fat_ptr = coerce(old_ptr);
319
320        let FatPtr { ptr, metadata } = FatPtr::from_ptr::<Inner<U>>(fat_ptr);
321
322        // SAFETY: `Inner` is `repr(C)` and has `metadata` at offset 0
323        unsafe { *(old_ptr as *mut usize) = metadata };
324
325        ThinCell {
326            // SAFETY: `ptr` is valid as it comes from `self`
327            ptr: unsafe { NonNull::new_unchecked(ptr) },
328            _marker: PhantomData,
329        }
330    }
331
332    /// Returns the raw pointer to the inner allocation.
333    pub fn as_ptr(&self) -> *const () {
334        self.ptr.as_ptr()
335    }
336
337    /// Returns `true` if the two `ThinCell`s point to the same allocation.
338    pub fn ptr_eq(&self, other: &Self) -> bool {
339        std::ptr::eq(self.as_ptr(), other.as_ptr())
340    }
341}
342
343impl<T: Any + ?Sized> ThinCell<T> {
344    /// Attempts to downcast the `ThinCell<T>` to `ThinCell<U>`.
345    ///
346    /// Returns `Some(ThinCell<U>)` if the inner value is of type `U`, or
347    /// `None` otherwise.
348    pub fn downcast<U: Any>(self) -> Option<ThinCell<U>> {
349        let inner = self.inner();
350        let data_ref = unsafe { &*inner.data.get() };
351
352        if TypeId::of::<U>() == data_ref.type_id() {
353            // SAFETY: We have verified that the inner value is of type `U`
354            Some(unsafe { self.downcast_unchecked::<U>() })
355        } else {
356            None
357        }
358    }
359}
360
361/// `ThinCell` is `Unpin` as it does not move its inner data.
362impl<T: ?Sized + Send> Unpin for ThinCell<T> {}
363
364impl<'a, T: ?Sized> Drop for Ref<'a, T> {
365    fn drop(&mut self) {
366        let current = self.state_cell.get();
367        self.state_cell.set(current.unborrow());
368    }
369}
370
371impl<'a, T: ?Sized> Deref for Ref<'a, T> {
372    type Target = T;
373
374    fn deref(&self) -> &T {
375        self.value
376    }
377}
378
379impl<'a, T: ?Sized> DerefMut for Ref<'a, T> {
380    fn deref_mut(&mut self) -> &mut T {
381        self.value
382    }
383}
384
385impl<'a, T: Debug + ?Sized> Debug for Ref<'a, T> {
386    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
387        Debug::fmt(&**self, f)
388    }
389}
390
391impl<'a, T: Display + ?Sized> Display for Ref<'a, T> {
392    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
393        Display::fmt(&**self, f)
394    }
395}
396
397impl<T: ?Sized> Clone for ThinCell<T> {
398    fn clone(&self) -> Self {
399        unsafe {
400            let inner = &*self.inner_ptr();
401            let current = inner.state.get();
402
403            match current.inc() {
404                Some(new_state) => inner.state.set(new_state),
405                None => panic!("Reference count overflow"),
406            }
407
408            ThinCell {
409                ptr: self.ptr,
410                _marker: PhantomData,
411            }
412        }
413    }
414}
415
416impl<T: ?Sized> Drop for ThinCell<T> {
417    fn drop(&mut self) {
418        unsafe {
419            let ptr = self.inner_ptr();
420            // SAFETY: pointer returned by `inner_ptr` is valid
421            let inner = &*ptr;
422            let current = inner.state.get();
423
424            // If count is 1, we are the last owner
425            if current.count() == 1 {
426                debug_assert!(!current.is_borrowed(), "Dropping while borrowed");
427                self.drop_in_place();
428            } else {
429                // Not last owner, decrement
430                inner.state.set(current.dec());
431            }
432        }
433    }
434}
435
436impl<T: Default> Default for ThinCell<T> {
437    fn default() -> Self {
438        ThinCell::new(T::default())
439    }
440}
441
442impl<T: Debug + ?Sized> Debug for ThinCell<T> {
443    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
444        let inner = self.inner();
445        let state = inner.state.get();
446        let mut d = f.debug_struct("ThinCell");
447        match self.try_borrow() {
448            Some(borrowed) => d.field("value", &borrowed),
449            None => d.field("value", &"<borrowed>"),
450        }
451        .field("state", &state)
452        .finish()
453    }
454}
455
456impl<T: Display + ?Sized> Display for ThinCell<T> {
457    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
458        match self.try_borrow() {
459            Some(borrowed) => Display::fmt(&*borrowed, f),
460            None => write!(f, "<borrowed>"),
461        }
462    }
463}
464
465impl<T: PartialEq + ?Sized> PartialEq<ThinCell<T>> for ThinCell<T> {
466    /// Compares the inner values for equality.
467    ///
468    /// # Panics
469    ///
470    /// Panics if either `ThinCell` is currently borrowed.
471    fn eq(&self, other: &Self) -> bool {
472        self.borrow().eq(&other.borrow())
473    }
474}
475
476impl<T: Eq + ?Sized> Eq for ThinCell<T> {}
477
478impl<T: Ord + ?Sized> PartialOrd<ThinCell<T>> for ThinCell<T> {
479    /// Compares the inner values.
480    ///
481    /// # Panics
482    ///
483    /// Panics if either `ThinCell` is currently borrowed.
484    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
485        self.borrow().partial_cmp(&other.borrow())
486    }
487}