abi_stable/sabi_types/
rsmallbox.rs

1//! Contains the `RSmallBox<_>` type.
2
3use crate::{
4    pointer_trait::{
5        AsMutPtr, AsPtr, CallReferentDrop, CanTransmuteElement, Deallocate, GetPointerKind,
6        OwnedPointer, PK_SmartPointer,
7    },
8    sabi_types::MovePtr,
9    std_types::RBox,
10};
11
12use std::{
13    alloc::{self, Layout},
14    fmt::{self, Display},
15    marker::PhantomData,
16    mem::{self, ManuallyDrop},
17    ops::{Deref, DerefMut},
18    ptr,
19};
20
21#[allow(unused_imports)]
22use core_extensions::SelfOps;
23
24use serde::{Deserialize, Deserializer, Serialize, Serializer};
25
26use crate::inline_storage::ScratchSpace;
27pub use crate::inline_storage::{alignment, InlineStorage};
28
29pub use self::private::RSmallBox;
30
31mod private {
32    use super::*;
33
34    ///
35    /// A box type which stores small values inline as an optimization.
36    ///
37    /// # Inline storage
38    ///
39    /// The `Inline` type parameter
40    /// is the storage space on the stack (as in inline with the `RSmallBox` struct)
41    /// where small values get stored,instead of storing them on the heap.
42    ///
43    /// It has to have an alignment greater than or equal to the value being stored,
44    /// otherwise storing the value on the heap.
45    ///
46    /// To ensure that the inline storage has enough alignemnt you can use one of the
47    /// `AlignTo*` types from the (reexported) alignment submodule,
48    /// or from `abi_stable::inline_storage::alignment`.
49    ///
50    /// # Examples
51    ///
52    /// ### In a nonexhaustive enum
53    ///
54    /// Using an RSmallBox to store a generic type in a nonexhaustive enum.
55    ///
56    /// ```
57    /// use abi_stable::{reexports::SelfOps, sabi_types::RSmallBox, std_types::RString, StableAbi};
58    ///
59    /// #[repr(u8)]
60    /// #[derive(StableAbi, Debug, Clone, PartialEq)]
61    /// #[sabi(kind(WithNonExhaustive(
62    ///     // Determines the maximum size of this enum in semver compatible versions.
63    ///     // This is 7 usize large because:
64    ///     //    - The enum discriminant occupies 1 usize(because the enum is usize aligned).
65    ///     //    - RSmallBox<T,[usize;4]>: is 6 usize large
66    ///     size = [usize;7],
67    ///     // Determines the traits that are required when wrapping this enum in NonExhaustive,
68    ///     // and are then available with it.
69    ///     traits(Debug,Clone,PartialEq),
70    /// )))]
71    /// #[non_exhaustive]
72    /// pub enum SomeEnum<T> {
73    ///     Foo,
74    ///     Bar,
75    ///     // This variant was added in a newer (compatible) version of the library.
76    ///     Baz(RSmallBox<T, [usize; 4]>),
77    /// }
78    ///
79    /// impl<T> SomeEnum<T> {
80    ///     pub fn is_inline(&self) -> bool {
81    ///         match self {
82    ///             SomeEnum::Foo => true,
83    ///             SomeEnum::Bar => true,
84    ///             SomeEnum::Baz(rsbox) => RSmallBox::is_inline(rsbox),
85    ///             _ => true,
86    ///         }
87    ///     }
88    ///
89    ///     pub fn is_heap_allocated(&self) -> bool {
90    ///         !self.is_inline()
91    ///     }
92    /// }
93    ///
94    /// #[repr(C)]
95    /// #[derive(StableAbi, Debug, Clone, PartialEq)]
96    /// pub struct FullName {
97    ///     pub name: RString,
98    ///     pub surname: RString,
99    /// }
100    ///
101    /// # fn main(){
102    ///
103    /// let rstring = "Oh boy!"
104    ///     .piped(RString::from)
105    ///     .piped(RSmallBox::new)
106    ///     .piped(SomeEnum::Baz);
107    ///
108    /// let full_name = FullName {
109    ///     name: "R__e".into(),
110    ///     surname: "L_____e".into(),
111    /// }
112    /// .piped(RSmallBox::new)
113    /// .piped(SomeEnum::Baz);
114    ///
115    /// assert!(rstring.is_inline());
116    /// assert!(full_name.is_heap_allocated());
117    ///
118    /// # }
119    ///
120    /// ```
121    ///
122    /// ### Trying out different `Inline` type parameters
123    ///
124    /// This example demonstrates how changing the `Inline` type parameter can
125    /// change whether an RString is stored inline or on the heap.
126    ///
127    /// ```
128    /// use abi_stable::{
129    ///     inline_storage::alignment::AlignToUsize, sabi_types::RSmallBox, std_types::RString,
130    ///     StableAbi,
131    /// };
132    ///
133    /// use std::mem;
134    ///
135    /// type JustRightInlineBox<T> = RSmallBox<T, AlignToUsize<[u8; mem::size_of::<usize>() * 4]>>;
136    ///
137    /// let string = RString::from("What is that supposed to mean?");
138    ///
139    /// let small = RSmallBox::<_, [usize; 3]>::new(string.clone());
140    /// let medium = RSmallBox::<_, [usize; 4]>::new(string.clone());
141    /// let large = RSmallBox::<_, [usize; 16]>::new(string.clone());
142    /// let not_enough_alignment = RSmallBox::<_, [u8; 64]>::new(string.clone());
143    /// let just_right = JustRightInlineBox::new(string.clone());
144    ///
145    /// assert!(RSmallBox::is_heap_allocated(&small));
146    /// assert!(RSmallBox::is_inline(&medium));
147    /// assert!(RSmallBox::is_inline(&large));
148    /// assert!(RSmallBox::is_heap_allocated(&not_enough_alignment));
149    /// assert!(RSmallBox::is_inline(&just_right));
150    ///
151    /// ```
152    #[repr(C)]
153    #[derive(StableAbi)]
154    #[sabi(not_stableabi(Inline))]
155    pub struct RSmallBox<T, Inline> {
156        // This is an opaque field since we only care about its size and alignment
157        #[sabi(unsafe_opaque_field)]
158        inline: ScratchSpace<(), Inline>,
159        ptr: *mut T,
160        destroy: unsafe extern "C" fn(*mut T, CallReferentDrop, Deallocate),
161        _marker: PhantomData<T>,
162    }
163
164    impl<T, Inline> RSmallBox<T, Inline> {
165        /// Constructs this RSmallBox from a value.
166        ///
167        /// # Example
168        ///
169        /// ```
170        /// use abi_stable::{sabi_types::RSmallBox, std_types::RString};
171        ///
172        /// let xbox = RSmallBox::<_, [usize; 4]>::new(RString::from("one"));
173        ///
174        /// ```
175        #[inline]
176        pub fn new(value: T) -> RSmallBox<T, Inline>
177        where
178            Inline: InlineStorage,
179        {
180            let mut value = ManuallyDrop::new(value);
181
182            unsafe { RSmallBox::from_move_ptr(MovePtr::new(&mut *value)) }
183        }
184
185        /// Gets a raw pointer into the underlying data.
186        ///
187        /// # Example
188        ///
189        /// ```
190        /// use abi_stable::{sabi_types::RSmallBox, std_types::RString};
191        ///
192        /// let mut play = RSmallBox::<_, [usize; 4]>::new(RString::from("station"));
193        ///
194        /// let play_addr = &mut play as *mut RSmallBox<_, _> as usize;
195        /// let heap_addr = RSmallBox::as_mut_ptr(&mut play) as usize;
196        ///
197        /// assert_eq!(play_addr, heap_addr);
198        ///
199        /// ```
200        #[inline]
201        pub fn as_mut_ptr(this: &mut Self) -> *mut T {
202            if this.ptr.is_null() {
203                &mut this.inline as *mut ScratchSpace<(), Inline> as *mut T
204            } else {
205                this.ptr
206            }
207        }
208
209        /// Gets a raw pointer into the underlying data.
210        ///
211        /// # Example
212        ///
213        /// ```
214        /// use abi_stable::{reexports::SelfOps, sabi_types::RSmallBox, std_types::RVec};
215        ///
216        /// let mut generations = vec![1, 2, 3, 4, 5, 6, 7, 8]
217        ///     .piped(RVec::from)
218        ///     .piped(RSmallBox::<_, [usize; 2]>::new);
219        ///
220        /// let generations_addr = &generations as *const RSmallBox<_, _> as usize;
221        /// let heap_addr = RSmallBox::as_ptr(&generations) as usize;
222        ///
223        /// assert_ne!(generations_addr, heap_addr);
224        ///
225        /// ```
226        #[inline]
227        pub fn as_ptr(this: &Self) -> *const T {
228            if this.ptr.is_null() {
229                &this.inline as *const ScratchSpace<(), Inline> as *const T
230            } else {
231                this.ptr
232            }
233        }
234
235        /// Constructs this `RSmallBox` from a `MovePtr`.
236        ///
237        /// # Example
238        ///
239        /// ```
240        /// use abi_stable::{pointer_trait::OwnedPointer, sabi_types::RSmallBox, std_types::RBox};
241        ///
242        /// let rbox = RBox::new(1000_u64);
243        /// let rsbox: RSmallBox<u64, [u64; 1]> =
244        ///     rbox.in_move_ptr(|x| RSmallBox::<u64, [u64; 1]>::from_move_ptr(x));
245        ///
246        /// assert_eq!(*rsbox, 1000_u64);
247        ///
248        /// ```
249        pub fn from_move_ptr(from_ptr: MovePtr<'_, T>) -> Self
250        where
251            Inline: InlineStorage,
252        {
253            let destroy = destroy::<T>;
254            let inline_size = mem::size_of::<Inline>();
255            let value_size = mem::size_of::<T>();
256
257            let inline_align = mem::align_of::<Inline>();
258            let value_align = mem::align_of::<T>();
259
260            unsafe {
261                let mut inline: ScratchSpace<(), Inline> = ScratchSpace::uninit();
262                let (storage_ptr, ptr) = if inline_size < value_size || inline_align < value_align {
263                    let x = alloc::alloc(Layout::new::<T>());
264                    (x, x as *mut T)
265                } else {
266                    (
267                        (&mut inline as *mut ScratchSpace<(), Inline> as *mut u8),
268                        ptr::null_mut(),
269                    )
270                };
271
272                (MovePtr::into_raw(from_ptr) as *const T as *const u8)
273                    .copy_to_nonoverlapping(storage_ptr, value_size);
274
275                Self {
276                    inline,
277                    ptr,
278                    destroy,
279                    _marker: PhantomData,
280                }
281            }
282        }
283
284        /// Converts this `RSmallBox` into another one with a differnet inline size.
285        ///
286        /// # Example
287        ///
288        /// ```
289        /// use abi_stable::sabi_types::RSmallBox;
290        ///
291        /// let old = RSmallBox::<u64, [u8; 4]>::new(599_u64);
292        /// assert!(!RSmallBox::is_inline(&old));
293        ///
294        /// let new = RSmallBox::move_::<[u64; 1]>(old);
295        /// assert!(RSmallBox::is_inline(&new));
296        /// assert_eq!(*new, 599_u64);
297        ///
298        /// ```
299        #[inline]
300        pub fn move_<Inline2>(this: Self) -> RSmallBox<T, Inline2>
301        where
302            Inline2: InlineStorage,
303        {
304            Self::with_move_ptr(ManuallyDrop::new(this), RSmallBox::from_move_ptr)
305        }
306
307        /// Queries whether the value is stored inline.
308        ///
309        /// # Example
310        ///
311        /// ```
312        /// use abi_stable::{sabi_types::RSmallBox, std_types::RString};
313        ///
314        /// let heap = RSmallBox::<u64, [u8; 4]>::new(599_u64);
315        /// assert!(!RSmallBox::is_inline(&heap));
316        ///
317        /// let inline = RSmallBox::<RString, [usize; 4]>::new("hello".into());
318        /// assert!(RSmallBox::is_inline(&inline));
319        ///
320        /// ```
321        pub fn is_inline(this: &Self) -> bool {
322            this.ptr.is_null()
323        }
324
325        /// Queries whether the value is stored on the heap.
326        ///
327        /// # Example
328        ///
329        /// ```
330        /// use abi_stable::{sabi_types::RSmallBox, std_types::RHashMap};
331        ///
332        /// let heap = RSmallBox::<_, [u8; 4]>::new(String::new());
333        /// assert!(RSmallBox::is_heap_allocated(&heap));
334        ///
335        /// let inline = RSmallBox::<_, [usize; 3]>::new(RHashMap::<u8, ()>::new());
336        /// assert!(!RSmallBox::is_heap_allocated(&inline));
337        ///
338        /// ```
339        pub fn is_heap_allocated(this: &Self) -> bool {
340            !this.ptr.is_null()
341        }
342
343        /// Unwraps this pointer into its owned value.
344        ///
345        /// # Example
346        ///
347        /// ```
348        /// use abi_stable::sabi_types::RSmallBox;
349        ///
350        /// let rbox = RSmallBox::<_, [usize; 3]>::new(vec![0, 1, 2]);
351        /// assert_eq!(RSmallBox::into_inner(rbox), vec![0, 1, 2]);
352        ///
353        /// ```
354        #[allow(clippy::redundant_closure)]
355        pub fn into_inner(this: Self) -> T {
356            Self::with_move_ptr(ManuallyDrop::new(this), |x| MovePtr::into_inner(x))
357        }
358
359        pub(super) unsafe fn drop_in_place(this: &mut Self, drop_referent: CallReferentDrop) {
360            let (ptr, dealloc) = if this.ptr.is_null() {
361                (
362                    &mut this.inline as *mut ScratchSpace<(), Inline> as *mut T,
363                    Deallocate::No,
364                )
365            } else {
366                (this.ptr, Deallocate::Yes)
367            };
368            unsafe { (this.destroy)(ptr, drop_referent, dealloc) };
369        }
370    }
371
372    /// Converts an RBox into an RSmallBox,currently this allocates.
373    impl<T, Inline> From<RBox<T>> for RSmallBox<T, Inline>
374    where
375        Inline: InlineStorage,
376    {
377        fn from(this: RBox<T>) -> Self {
378            RBox::with_move_ptr(ManuallyDrop::new(this), Self::from_move_ptr)
379        }
380    }
381
382    /// Converts a RSmallBox into an RBox,currently this allocates.
383    impl<T, Inline> From<RSmallBox<T, Inline>> for RBox<T>
384    where
385        Inline: InlineStorage,
386    {
387        fn from(this: RSmallBox<T, Inline>) -> RBox<T> {
388            OwnedPointer::with_move_ptr(ManuallyDrop::new(this), |x| MovePtr::into_rbox(x))
389        }
390    }
391}
392
393///////////////////////////////////////////////////////////////////////////////
394
395unsafe impl<T, Inline> GetPointerKind for RSmallBox<T, Inline> {
396    type Kind = PK_SmartPointer;
397
398    type PtrTarget = T;
399}
400
401impl<T, Inline> Deref for RSmallBox<T, Inline> {
402    type Target = T;
403
404    fn deref(&self) -> &T {
405        unsafe { &*Self::as_ptr(self) }
406    }
407}
408
409impl<T, Inline> DerefMut for RSmallBox<T, Inline> {
410    fn deref_mut(&mut self) -> &mut T {
411        unsafe { &mut *Self::as_mut_ptr(self) }
412    }
413}
414
415unsafe impl<T, Inline> AsPtr for RSmallBox<T, Inline> {
416    fn as_ptr(&self) -> *const T {
417        Self::as_ptr(self)
418    }
419}
420
421unsafe impl<T, Inline> AsMutPtr for RSmallBox<T, Inline> {
422    fn as_mut_ptr(&mut self) -> *mut T {
423        Self::as_mut_ptr(self)
424    }
425}
426
427impl<T, Inline> Default for RSmallBox<T, Inline>
428where
429    T: Default,
430    Inline: InlineStorage,
431{
432    fn default() -> Self {
433        Self::new(T::default())
434    }
435}
436
437impl<T, Inline> Clone for RSmallBox<T, Inline>
438where
439    T: Clone,
440    Inline: InlineStorage,
441{
442    fn clone(&self) -> Self {
443        RSmallBox::new((**self).clone())
444    }
445}
446
447impl<T, Inline> Display for RSmallBox<T, Inline>
448where
449    T: Display,
450{
451    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
452        Display::fmt(&**self, f)
453    }
454}
455
456shared_impls! {
457    mod=box_impls
458    new_type=RSmallBox[][T,Inline],
459    original_type=Box,
460}
461
462unsafe impl<T, O, Inline> CanTransmuteElement<O> for RSmallBox<T, Inline> {
463    type TransmutedPtr = RSmallBox<O, Inline>;
464
465    unsafe fn transmute_element_(self) -> Self::TransmutedPtr {
466        unsafe { core_extensions::utils::transmute_ignore_size(self) }
467    }
468}
469
470unsafe impl<T: Send, Inline> Send for RSmallBox<T, Inline> {}
471unsafe impl<T: Sync, Inline> Sync for RSmallBox<T, Inline> {}
472
473///////////////////////////////////////////////////////////////////////////////
474
475impl<T, Inline> Serialize for RSmallBox<T, Inline>
476where
477    T: Serialize,
478{
479    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
480    where
481        S: Serializer,
482    {
483        (**self).serialize(serializer)
484    }
485}
486
487impl<'de, T, Inline> Deserialize<'de> for RSmallBox<T, Inline>
488where
489    Inline: InlineStorage,
490    T: Deserialize<'de>,
491{
492    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
493    where
494        D: Deserializer<'de>,
495    {
496        T::deserialize(deserializer).map(Self::new)
497    }
498}
499
500//////////////////////////////////////////////////////////////////////////////
501
502unsafe impl<T, Inline> OwnedPointer for RSmallBox<T, Inline> {
503    #[inline]
504    unsafe fn get_move_ptr(this: &mut ManuallyDrop<Self>) -> MovePtr<'_, T> {
505        unsafe { MovePtr::new(&mut **this) }
506    }
507
508    #[inline]
509    unsafe fn drop_allocation(this: &mut ManuallyDrop<Self>) {
510        unsafe {
511            Self::drop_in_place(&mut **this, CallReferentDrop::No);
512        }
513    }
514}
515
516impl<T, Inline> Drop for RSmallBox<T, Inline> {
517    fn drop(&mut self) {
518        unsafe {
519            Self::drop_in_place(self, CallReferentDrop::Yes);
520        }
521    }
522}
523
524unsafe extern "C" fn destroy<T>(ptr: *mut T, drop_referent: CallReferentDrop, dealloc: Deallocate) {
525    extern_fn_panic_handling! {no_early_return;
526        if let CallReferentDrop::Yes=drop_referent{
527            unsafe { ptr::drop_in_place(ptr); }
528        }
529        if let Deallocate::Yes = dealloc{
530            unsafe { drop(Box::from_raw(ptr as *mut ManuallyDrop<T>)); }
531        }
532    }
533}
534
535//////////////////////////////////////////////////////////////////////////////
536
537#[cfg(all(test, not(feature = "only_new_tests")))]
538mod tests;