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(¬_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;