// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md. use std::any::TypeId; use std::mem::{forget, size_of, MaybeUninit}; use std::ptr::{drop_in_place, read, write}; /// A statically sized container that acts like Box but without heap overhead. /// /// This is used in a couple places to avoid what would otherwise be an explosion of cascading /// template parameters to support externally defined small objects. It does so with virtually /// no overhead, unlike Box<>, and could be no_std compatible. /// /// This will panic if the capacity is too small. If that occurs, it must be enlarged. It will /// also panic if any of the accessors (other than the try_ versions) are used to try to get /// a type other than the one it was constructed with. #[repr(C)] pub struct Thing { storage: [u8; CAPACITY], dropper: fn(*mut u8), data_type: TypeId, } impl Thing { #[inline(always)] pub fn new(x: T) -> Self { assert!(size_of::() <= CAPACITY); let mut p = Self { storage: unsafe { MaybeUninit::uninit().assume_init() }, dropper: |s: *mut u8| unsafe { drop_in_place::((*s.cast::()).storage.as_mut_ptr().cast()); }, data_type: TypeId::of::(), }; unsafe { write(p.storage.as_mut_ptr().cast(), x) }; p } #[inline(always)] pub fn get(&self) -> &T { assert_eq!(TypeId::of::(), self.data_type); unsafe { &*self.storage.as_ptr().cast() } } #[inline(always)] pub fn get_mut(&mut self) -> &mut T { assert_eq!(TypeId::of::(), self.data_type); unsafe { &mut *self.storage.as_mut_ptr().cast() } } #[inline(always)] pub fn try_get(&self) -> Option<&T> { if TypeId::of::() == self.data_type { Some(unsafe { &*self.storage.as_ptr().cast() }) } else { None } } #[inline(always)] pub fn try_get_mut(&mut self) -> Option<&mut T> { if TypeId::of::() == self.data_type { Some(unsafe { &mut *self.storage.as_mut_ptr().cast() }) } else { None } } #[inline(always)] pub fn unwrap(self) -> T { assert_eq!(TypeId::of::(), self.data_type); let x = unsafe { read(self.storage.as_ptr().cast()) }; forget(self); x } } impl AsRef for Thing { #[inline(always)] fn as_ref(&self) -> &T { assert_eq!(TypeId::of::(), self.data_type); unsafe { &*self.storage.as_ptr().cast() } } } impl AsMut for Thing { #[inline(always)] fn as_mut(&mut self) -> &mut T { assert_eq!(TypeId::of::(), self.data_type); unsafe { &mut *self.storage.as_mut_ptr().cast() } } } impl Drop for Thing { #[inline(always)] fn drop(&mut self) { (self.dropper)((self as *mut Self).cast()); } } #[cfg(test)] mod tests { use super::*; use std::rc::Rc; #[test] fn typing_and_life_cycle() { let test_obj = Rc::new(1i32); assert_eq!(Rc::strong_count(&test_obj), 1); let a = Thing::<32>::new(test_obj.clone()); let b = Thing::<32>::new(test_obj.clone()); let c = Thing::<32>::new(test_obj.clone()); assert!(a.get::>().eq(b.get())); assert!(a.try_get::>().is_some()); assert!(a.try_get::>().is_none()); assert_eq!(Rc::strong_count(&test_obj), 4); drop(a); assert_eq!(Rc::strong_count(&test_obj), 3); drop(b); assert_eq!(Rc::strong_count(&test_obj), 2); let c = c.unwrap::>(); assert_eq!(Rc::strong_count(&test_obj), 2); drop(c); assert_eq!(Rc::strong_count(&test_obj), 1); } }