Skip to main content

variadic_arguments/argument/
owned.rs

1#[cfg(no_std)]
2use ::alloc::{alloc, boxed::Box};
3
4#[cfg(no_std)]
5use core::{
6    any::Any,
7    fmt,
8    mem,
9    ptr::NonNull,
10    ops
11};
12
13#[cfg(not(no_std))]
14use std::{
15    alloc,
16    fmt,
17    any::Any,
18    mem,
19    ptr::NonNull,
20    ops
21};
22
23use super::{
24    discriminant::Discriminant,
25    boxed_argument::BoxedArgument,
26    inlined::Inlined,
27    variant_info::VariantHandle
28};
29
30/// An owned argument.
31///
32/// This carries a generic item that implements both Any and Clone.
33/// In addition, depending on the storage itself, it is able to implement
34/// items whose size is no more than 8 bytes for 64-bit systems (or 4 for 32-bit systems).
35pub struct OwnedArgument
36{
37    /// Pointer storage. This acts as a wrapper for both
38    /// inlined and boxed storage. Due to inline behavior, we
39    /// cannot use this pointer directly.
40    pointer: *mut dyn VariantHandle,
41    inlined: bool,
42    owned: bool
43}
44
45impl fmt::Debug for OwnedArgument
46{
47    #[inline(always)]
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
49    {
50        let mut current = f.debug_struct("OwnedArgument");
51                 
52        current.field("is_inlined", &self.inlined);
53        
54        let ref_ : &dyn Any =
55        unsafe { self.pointer().as_ref() };
56        
57        current.field("storage", &ref_);
58        
59        current.finish()
60    }
61}
62
63impl Clone for OwnedArgument
64{
65    #[inline(always)]
66    fn clone(&self) -> Self
67    {
68        unsafe
69        {
70            self.pointer()
71                .as_ref()
72                .clone_object()
73        }
74    }
75}
76
77impl Drop for OwnedArgument
78{
79    #[inline(always)]
80    fn drop(&mut self)
81    {
82        let _ =
83        unsafe
84        {
85            BoxedArgument::from_owned(self.pointer,
86                                      self.owned_discriminant())
87        };
88    }
89}
90
91
92#[cfg(debug_assertions)]
93fn pointer_matches<T>(pointer: *mut dyn VariantHandle) -> bool
94where
95    T: Any + Clone
96{
97    assert!(!pointer.is_null());
98    
99    let pointer : *const dyn Any =
100    pointer.cast_const() as *const _ as *const dyn Any;
101    
102    let ref_ = unsafe { &*pointer };
103    
104    ref_.is::<T>()
105}
106
107impl OwnedArgument
108{
109    /// Creates a new OwnedArgument based around a generic item.
110    ///
111    /// If the size of said item is less than 8 bytes for 64-bit systems (4 for 32-bit systems),
112    /// then the storage is inlined. Otherwise, the storage gets allocated instead.
113    #[inline(always)]
114    pub fn new<T>(item: T) -> Self
115    where
116        T: Any + Clone
117    {
118        if size_of::<T>() <= size_of::<*const ()>()
119        {
120            let mut store =
121            mem::MaybeUninit::<*mut dyn VariantHandle>::new((&raw const item).cast_mut());
122
123            unsafe
124            {
125                store.as_mut_ptr().cast::<T>().write(item);
126            }
127
128            Self
129            {
130                pointer:
131                unsafe
132                {
133                    store.assume_init()
134                },
135                inlined: true,
136                owned: true
137            }
138        }
139        else
140        {
141            let boxed : Box<dyn VariantHandle> = Box::new(item);
142            
143            let pointer = Box::into_raw(boxed);
144            
145            Self
146            {
147                pointer,
148                inlined: false,
149                owned: true
150            }
151        }
152    }
153
154    #[inline(always)]
155    unsafe fn pointer_metadata(&self) -> *mut dyn VariantHandle
156    {
157        self.pointer
158    }
159
160    #[inline(always)]
161    fn pointer(&self) -> NonNull<dyn VariantHandle>
162    {
163        match self.owned_discriminant()
164        {
165            Discriminant::Inlined =>
166            unsafe { self.inner_inlined().pointer() },
167            Discriminant::Allocated =>
168            unsafe { NonNull::new_unchecked(self.pointer) },
169            _ => unreachable!()
170        }
171    }
172    
173    /// Acquires the discriminant of the OwnedPointer.
174    ///
175    /// This should not return Discriminant::Borrowed.
176    #[inline(always)]
177    pub(crate) fn owned_discriminant(&self) -> Discriminant
178    {
179        Discriminant::from_owned(self.inlined)
180    }
181    
182    /// Acquires the discriminant based around the OwnedPointer's storage information.
183    #[inline(always)]
184    pub(crate) fn discriminant(&self) -> Discriminant
185    {
186        Discriminant::from_info((self.inlined, self.owned))
187    }
188    
189    /// Checks if the storage is inlined or not.
190    ///
191    /// This is only used for testing purposes.
192    #[cfg(test)]
193    pub(crate) fn is_inlined(&self) -> bool
194    {
195        self.inlined
196    }
197    
198    /// Acquires the inner pointer to the inlined storage.
199    ///
200    /// # Safety
201    /// For accessing information, such as owned and inlined status,
202    /// this is guaranteed to be safe. Otherwise, this function assumes
203    /// that the storage is inlined.
204    #[inline(always)]
205    unsafe fn inner_inlined(&self) -> &Inlined
206    {
207        unsafe
208        {
209            &*(&raw const self.pointer)
210                .cast::<Inlined>()
211        }
212    }
213
214    /// A "wrapper" for `Any::is::<T>()`.
215    ///
216    /// In case Any interferes with dereferencing the OwnedArgument, use the following function instead.
217    #[inline(always)]
218    pub fn is_type<T>(&self) -> bool
219    where
220        T: Any + Clone
221    {
222        unsafe
223        {
224            let metadata : *const dyn Any =
225            self.pointer_metadata().cast_const() as *const _ as *const dyn Any;
226
227            (*metadata).is::<T>()
228        }
229    }
230    
231    /// Acquires a raw reference handle to the object itself.
232    ///
233    /// This is useful for internally creating references to VariantHandle.
234    #[inline(always)]
235    pub(crate) fn raw_ref(&self) -> &dyn VariantHandle
236    {
237        unsafe
238        {
239            self.pointer()
240                .as_ref()
241        }
242    }
243    
244    /// Downcasts the object into an owned instance.
245    ///
246    /// # Return values:
247    /// Ok(val): The value matches is T, and the previous storage frees itself.
248    /// Err(self): The value does not match T, the inner value should remain identical.
249    #[inline(always)]
250    pub fn downcast_owned<T>(self) -> Result<T, Self>
251    where
252        T: Any + Clone
253    {
254        if self.is_type::<T>()
255        {
256            unsafe
257            {
258               Ok(self.downcast_owned_unchecked())
259            }
260        } else { Err(self) }
261    }
262    
263    /// Downcasts the inner value into T without checking it first.
264    ///
265    /// # Safety
266    /// This assumes that the type supplied is, in fact, T.
267    #[inline(always)]
268    pub unsafe fn downcast_owned_unchecked<T>(self) -> T
269    where
270        T: Any + Clone
271    {
272        let owned = mem::ManuallyDrop::new(self);
273        
274        let boxed =
275        unsafe
276        {
277            BoxedArgument::from_owned(owned.pointer,
278                                      owned.owned_discriminant())
279        };
280        
281        match boxed
282        {
283            BoxedArgument::Allocated(a) =>
284            {
285                let raw_pointer = Box::into_raw(a);
286                
287                #[cfg(debug_assertions)]
288                {
289                    assert!(pointer_matches::<T>(raw_pointer));
290                }
291                
292                let output =
293                {
294                    let pointer : *mut T =
295                    raw_pointer.cast();
296                    
297                    unsafe
298                    {
299                        pointer.read_unaligned()
300                    }
301                };
302                
303                let layout = alloc::Layout::new::<T>();
304                
305                unsafe
306                {
307                    alloc::dealloc(raw_pointer.cast::<u8>(),
308                                   layout);
309                }
310                
311                output
312            }
313            BoxedArgument::Inlined(i) =>
314            {
315                #[cfg(debug_assertions)]
316                {
317                    let raw_pointer = i.pointer().as_ptr();
318                    assert!(pointer_matches::<T>(raw_pointer));
319                }
320                
321                let store = mem::ManuallyDrop::new(i);
322                
323                let pointer = &raw const store;
324                
325                unsafe
326                {
327                    pointer.cast::<T>().read()
328                }
329            }
330        }
331    }
332    
333    /// Downcasts a reference of the OwnedArgument before returning the cloned contents of the inner value:
334    ///
335    /// # Return values
336    /// Some(v): The cloned object is of type T,
337    /// None: OwnedArgument is not type T
338    #[inline(always)]
339    pub fn downcast_cloned<T>(&self) -> Option<T>
340    where
341        T: Any + Clone
342    {
343        if self.is_type::<T>()
344        {
345            unsafe
346            {
347                Some(self.downcast_cloned_unchecked())
348            }
349        }
350        else { None }
351    }
352    
353    /// Returns the cloned contents of the inner type of an OwnedArgument without performing any checks.
354    ///
355    /// # Safety
356    /// This assumes that the OwnedArgument is type T.
357    #[inline(always)]
358    pub unsafe fn downcast_cloned_unchecked<T>(&self) -> T
359    where
360        T: Any + Clone
361    {
362        let pointer = self.pointer();
363        
364        #[cfg(debug_assertions)]
365        {
366            assert!(pointer_matches::<T>(pointer.as_ptr()));
367        }
368        
369        unsafe
370        {
371            pointer.cast::<T>().as_ref().clone()
372        }
373    }
374}
375
376
377impl ops::Deref for OwnedArgument
378{
379    type Target = dyn Any;
380    
381    #[inline(always)]
382    fn deref(&self) -> &dyn Any
383    {
384        unsafe
385        {
386            self.pointer()
387                .as_ref()
388        }
389    }
390}
391
392
393impl ops::DerefMut for OwnedArgument
394{
395    #[inline(always)]
396    fn deref_mut(&mut self) -> &mut dyn Any
397    {
398        unsafe
399        {
400            self.pointer()
401                .as_mut()
402        }
403    }
404}
405