any_box/
lib.rs

1//! The library contains an implementation of a container type `AnyBox` which works similar to C++'s (std::any)
2
3pub use std::any::TypeId;
4use std::{fmt, marker::PhantomData, mem, ptr::NonNull};
5
6////////////////////////////////////////////////////////////////////////////////
7// AnyBox
8
9#[doc(hidden)]
10mod internal {
11    use super::{mem, NonNull, TypeId};
12    use std::marker::PhantomData;
13
14    /// A trait for markers to decide if AnyBox is Send
15    pub trait SendMarker {}
16
17    impl SendMarker for () {}
18
19    ///
20    pub(super) enum OpCode {
21        Drop,
22        IsTypeId,
23        GetTypeId,
24    }
25
26    ///
27    pub(super) type ManagerFn = fn(OpCode, NonNull<u8>) -> bool;
28
29    ///
30    pub(super) struct InlineManager<T: Sized + 'static>(PhantomData<T>);
31    impl<T: Sized + 'static> InlineManager<T> {
32        // const TYPE_ID: TypeId = TypeId::of::<T>();
33
34        #[allow(non_snake_case)]
35        pub fn do_op(code: OpCode, arg_ptr: NonNull<u8>) -> bool {
36            let TYPE_ID: TypeId = TypeId::of::<T>();
37
38            match code {
39                OpCode::Drop => unsafe {
40                    // safety: drop is done on a valid instance, so pointer must be valid
41
42                    let ptr: *mut T = mem::transmute(arg_ptr.as_ptr());
43                    std::ptr::drop_in_place(ptr);
44                    true
45                },
46                OpCode::GetTypeId => unsafe {
47                    let ptr: *mut TypeId = mem::transmute(arg_ptr.as_ptr());
48                    (*ptr) = TYPE_ID;
49                    true
50                },
51                OpCode::IsTypeId => unsafe {
52                    let ptr: *mut TypeId = mem::transmute(arg_ptr.as_ptr());
53                    (*ptr) == TYPE_ID
54                },
55            }
56        }
57    }
58
59    ///
60    pub(super) struct BoxedManager<T: Sized + 'static>(PhantomData<T>);
61    impl<T: Sized + 'static> BoxedManager<T> {
62        // const TYPE_ID: TypeId = TypeId::of::<T>();
63
64        #[allow(non_snake_case)]
65        pub fn do_op(code: OpCode, arg_ptr: NonNull<u8>) -> bool {
66            let TYPE_ID: TypeId = TypeId::of::<T>();
67
68            match code {
69                OpCode::Drop => unsafe {
70                    // safety: drop is done on a valid instance, so pointer must be valid
71
72                    let ptr: *mut Box<T> = mem::transmute(arg_ptr.as_ptr());
73                    std::ptr::drop_in_place(ptr);
74                    true
75                },
76                OpCode::GetTypeId => unsafe {
77                    let ptr: *mut TypeId = mem::transmute(arg_ptr.as_ptr());
78                    (*ptr) = TYPE_ID;
79                    true
80                },
81                OpCode::IsTypeId => unsafe {
82                    let ptr: *mut TypeId = mem::transmute(arg_ptr.as_ptr());
83                    (*ptr) == TYPE_ID
84                },
85            }
86        }
87    }
88
89    ///
90    #[inline]
91    pub(super) fn get_void_ptr<T: Sized>(item: &T) -> NonNull<u8> {
92        unsafe { NonNull::new_unchecked(mem::transmute(&*item)) }
93    }
94
95    ///
96    #[inline]
97    pub(super) fn get_ptr<T: Sized>(item: &T) -> NonNull<T> {
98        unsafe { NonNull::new_unchecked(mem::transmute(&*item)) }
99    }
100
101    ///
102    pub(super) const PTR_SIZE: usize = mem::size_of::<*mut u8>();
103}
104
105use internal::SendMarker;
106
107/// Marker for NoSend AnyBox
108pub struct NoSend(*const u8); // Makes !Send
109impl SendMarker for NoSend {}
110
111/// To construct empty AnyBox
112struct Empty;
113
114/// A container for any instance works similar to C++'s `std::any` and is made to replace `Arc<dyn Any>`.
115/// The key difference from `Arc<dyn Any>` is that small instances (up to 8 bytes) are stored in the `AnyBox` instance
116#[derive(Debug)]
117pub struct AnyBox<M: SendMarker = ()> {
118    manager: internal::ManagerFn,
119    storage: [u8; internal::PTR_SIZE], // DTS is not allowed
120    // (see https://doc.rust-lang.org/reference/dynamically-sized-types.html)
121    marker: PhantomData<M>,
122}
123
124// public interface
125
126impl<M: SendMarker> Drop for AnyBox<M> {
127    fn drop(&mut self) {
128        let data_ptr = internal::get_void_ptr(&self.storage[0]);
129        (self.manager)(internal::OpCode::Drop, data_ptr);
130    }
131}
132
133impl AnyBox<()> {
134    ///
135    pub fn new<T: Send + Sized + 'static>(item: T) -> Self {
136        let mut this = mem::MaybeUninit::<Self>::zeroed();
137
138        // safety: this is created on stack so it must have a non null adress
139        let this_ptr = unsafe { NonNull::new_unchecked(this.as_mut_ptr()) };
140
141        Self::_set_data(this_ptr, item);
142
143        // safety: the code above ensures initialization of the instance
144        unsafe { this.assume_init() }
145    }
146}
147
148impl AnyBox<NoSend> {
149    ///
150    pub fn new_nosend<T: Sized + 'static>(item: T) -> Self {
151        let mut this = mem::MaybeUninit::<Self>::zeroed();
152
153        // safety: this is created on stack so it must have a non null adress
154        let this_ptr = unsafe { NonNull::new_unchecked(this.as_mut_ptr()) };
155
156        Self::_set_data(this_ptr, item);
157
158        // safety: the code above ensures initialization of the instance
159        unsafe { this.assume_init() }
160    }
161}
162
163impl<M: SendMarker> AnyBox<M> {
164    /// Creates an empty box (initialized with empty tuple) - can be used for delayed initialization.
165    /// The instance doesn't require a drop call as the empty tuple instance will be places inte the box's internal storage,
166    /// and the tuple instance doesn't require dropping.
167    pub fn empty() -> Self {
168        let mut this = mem::MaybeUninit::<Self>::zeroed();
169
170        // safety: this is created on stack so it must have a non null adress
171        let this_ptr = unsafe { NonNull::new_unchecked(this.as_mut_ptr()) };
172
173        Self::_set_data(this_ptr, Empty);
174
175        // safety: the code above ensures initialization of the instance
176        unsafe { this.assume_init() }
177    }
178
179    /// Returns a [`TypeId`] instance associated with a stored value.
180    pub fn get_type_id(&self) -> TypeId {
181        let mut type_id = mem::MaybeUninit::<TypeId>::uninit();
182        // safety: the object is on stack, so it's not a null
183        (self.manager)(internal::OpCode::GetTypeId, unsafe {
184            NonNull::new_unchecked(type_id.as_mut_ptr() as *mut u8)
185        });
186        // safety: initialization is done by manager
187        unsafe { type_id.assume_init() }
188    }
189
190    /// Checks whether the instance holds nothing
191    pub fn is_empty(&self) -> bool {
192        self.is::<Empty>()
193    }
194
195    /// Checks whether the instance holds an instance of a type `T`
196    pub fn is<T: Sized + 'static>(&self) -> bool {
197        let type_id = TypeId::of::<T>();
198        (self.manager)(internal::OpCode::IsTypeId, internal::get_void_ptr(&type_id))
199    }
200
201    /// Returns a borrow of a stored value if it's an instance of a type `T`
202    pub fn get<T: Sized + 'static>(&self) -> Option<&T> {
203        match self.is::<T>() {
204            false => None,
205            true => Some(unsafe { self.get_unchecked() }),
206        }
207    }
208
209    /// Returns a borrow of a stored value if it's an instance of a type `T`
210    ///
211    /// ### Safety:
212    /// User must be sure that the instance holds a value of `T`
213    pub unsafe fn get_unchecked<T: Sized + 'static>(&self) -> &T {
214        let data_ptr = Self::_get_data(internal::get_ptr(self));
215        &*data_ptr.as_ptr()
216    }
217
218    ///
219    pub fn get_mut<T: Sized + 'static>(&mut self) -> Option<&mut T> {
220        match self.is::<T>() {
221            false => None,
222            true => Some(unsafe { self.get_mut_unchecked() }),
223        }
224    }
225
226    ///
227    pub unsafe fn get_mut_unchecked<T: Sized + 'static>(&mut self) -> &mut T {
228        let data_ptr = Self::_get_data(internal::get_ptr(self));
229        &mut *data_ptr.as_ptr()
230    }
231
232    ///
233    pub fn take<T: Sized + 'static>(self) -> Result<T, Self> {
234        match self.is::<T>() {
235            false => Err(self),
236            true => Ok(unsafe { self.take_unchecked() }),
237        }
238    }
239
240    ///
241    pub unsafe fn take_unchecked<T: Sized + 'static>(self) -> T {
242        // preventing double-freeing
243        let this = mem::ManuallyDrop::new(self);
244        Self::_take_data(internal::get_ptr(&this))
245    }
246}
247
248// internal interface
249
250impl<M: SendMarker> AnyBox<M> {
251    fn _set_data<T: Sized + 'static>(this_ptr: NonNull<Self>, item: T) {
252        let this_ptr = this_ptr.as_ptr();
253        // safety: this_ptr is non null
254        unsafe {
255            let size: usize = mem::size_of::<T>();
256            if size > internal::PTR_SIZE {
257                let storage_ptr = mem::transmute(&(*this_ptr).storage[0]);
258                // moving item to a heap to fit size requirements
259                let item = Box::new(item);
260                std::ptr::write(storage_ptr, item);
261
262                (*this_ptr).manager = internal::BoxedManager::<T>::do_op;
263            } else {
264                let storage_ptr = mem::transmute(&(*this_ptr).storage[0]);
265                std::ptr::write(storage_ptr, item);
266
267                (*this_ptr).manager = internal::InlineManager::<T>::do_op;
268            }
269        }
270    }
271
272    fn _get_data<T: Sized + 'static>(this_ptr: NonNull<Self>) -> NonNull<T> {
273        let this_ptr = this_ptr.as_ptr();
274        // safety: this_ptr is non null
275        unsafe {
276            let storage_ptr: *mut u8 = &mut (*this_ptr).storage[0];
277
278            let size: usize = mem::size_of::<T>();
279            let item_ptr = if size > internal::PTR_SIZE {
280                let ptr: *mut Box<T> = mem::transmute(storage_ptr);
281                (*ptr).as_mut() as *mut T
282            } else {
283                let ptr: *mut T = mem::transmute(storage_ptr);
284                ptr
285            };
286
287            NonNull::new_unchecked(item_ptr)
288        }
289    }
290
291    fn _take_data<T: Sized + 'static>(this_ptr: NonNull<Self>) -> T {
292        let this_ptr = this_ptr.as_ptr();
293        // safety: this_ptr is non null
294        unsafe {
295            let storage_ptr: *mut u8 = &mut (*this_ptr).storage[0];
296
297            let size: usize = mem::size_of::<T>();
298            let item = if size > internal::PTR_SIZE {
299                let ptr: *mut Box<T> = mem::transmute(storage_ptr);
300                let ptr: Box<T> = std::ptr::read(ptr);
301                // moving out the boxed value
302                *ptr
303            } else {
304                let ptr: *mut T = mem::transmute(storage_ptr);
305                std::ptr::read(ptr)
306            };
307
308            item
309        }
310    }
311}
312
313// impl traits
314
315impl fmt::Display for AnyBox {
316    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
317        let type_id = unsafe {
318            let mut val = mem::MaybeUninit::<TypeId>::uninit();
319            (self.manager)(
320                internal::OpCode::GetTypeId,
321                NonNull::new_unchecked(mem::transmute(val.as_mut_ptr())),
322            );
323            val.assume_init()
324        };
325
326        write!(f, "AnyBox::<{:?}>", type_id)
327    }
328}
329
330//
331////////////////////////////////////////////////////////////////////////////////
332
333////////////////////////////////////////////////////////////////////////////////
334// Unit tests
335
336#[cfg(test)]
337mod tests {
338    use super::AnyBox;
339    use serial_test::serial;
340    use std::sync::atomic::{AtomicUsize, Ordering};
341
342    // to prevent seg faults with Arc if AnyBox works incorrectly
343    static _ITEM_NEW_COUNTER: AtomicUsize = AtomicUsize::new(0);
344    static _ITEM_DROP_COUNTER: AtomicUsize = AtomicUsize::new(0);
345
346    struct Item<T> {
347        _data: T,
348    }
349
350    impl<T: Default> Item<T> {
351        fn new() -> Self {
352            _ITEM_NEW_COUNTER.fetch_add(1, Ordering::Relaxed);
353            Item {
354                _data: T::default(),
355            }
356        }
357    }
358
359    impl<T> Drop for Item<T> {
360        fn drop(&mut self) {
361            _ITEM_DROP_COUNTER.fetch_add(1, Ordering::Relaxed);
362        }
363    }
364
365    // tests
366
367    #[test]
368    fn size_of() {
369        const PTR_SIZE_X2: usize = std::mem::size_of::<*mut u8>() << 1;
370        assert_eq!(std::mem::size_of::<AnyBox>(), PTR_SIZE_X2);
371        assert_eq!(std::mem::size_of::<Option<AnyBox>>(), PTR_SIZE_X2);
372        assert_eq!(
373            std::mem::size_of::<std::sync::Arc<dyn std::any::Any>>(),
374            PTR_SIZE_X2
375        );
376    }
377
378    #[test]
379    #[serial]
380    fn size_8_byte() {
381        _ITEM_NEW_COUNTER.store(0, Ordering::Release);
382        _ITEM_DROP_COUNTER.store(0, Ordering::Release);
383
384        {
385            let p = AnyBox::new(Item::<usize>::new());
386            assert!(p.is::<Item<usize>>());
387            assert!(!p.is::<isize>());
388        }
389
390        {
391            let p = AnyBox::new(Item::<usize>::new());
392            let item = p.take::<usize>();
393            assert!(!item.is_ok());
394
395            if let Err(p) = item {
396                let item = p.take::<Item<usize>>();
397                assert!(item.is_ok());
398            }
399        }
400
401        assert_eq!(_ITEM_NEW_COUNTER.load(Ordering::Relaxed), 2);
402        assert_eq!(
403            _ITEM_NEW_COUNTER.load(Ordering::Relaxed),
404            _ITEM_DROP_COUNTER.load(Ordering::Relaxed)
405        );
406    }
407
408    #[test]
409    #[serial]
410    fn size_16_byte() {
411        _ITEM_NEW_COUNTER.store(0, Ordering::Release);
412        _ITEM_DROP_COUNTER.store(0, Ordering::Release);
413
414        {
415            let p = AnyBox::new(Item::<(usize, usize)>::new());
416            assert!(p.is::<Item<(usize, usize)>>());
417            assert!(!p.is::<isize>());
418        }
419
420        {
421            let p = AnyBox::new(Item::<(usize, usize)>::new());
422            let item = p.take::<usize>();
423            assert!(!item.is_ok());
424
425            if let Err(p) = item {
426                let item = p.take::<Item<(usize, usize)>>();
427                assert!(item.is_ok());
428            }
429        }
430
431        assert_eq!(_ITEM_NEW_COUNTER.load(Ordering::Relaxed), 2);
432        assert_eq!(
433            _ITEM_NEW_COUNTER.load(Ordering::Relaxed),
434            _ITEM_DROP_COUNTER.load(Ordering::Relaxed)
435        );
436    }
437}
438
439//
440////////////////////////////////////////////////////////////////////////////////