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}