#![cfg_attr(not(feature = "std"), no_std)]
#[derive(Debug)]
pub struct StackAny<const N: usize> {
type_id: core::any::TypeId,
bytes: [core::mem::MaybeUninit<u8>; N],
drop_fn: fn(*mut core::mem::MaybeUninit<u8>) -> (),
}
impl<const N: usize> StackAny<N> {
pub fn try_new<T>(value: T) -> Option<Self>
where
T: core::any::Any,
{
let type_id = core::any::TypeId::of::<T>();
let size = core::mem::size_of::<T>();
if N < size {
return None;
}
let mut bytes = [core::mem::MaybeUninit::uninit(); N];
let src = &value as *const _ as *const _;
let dst = bytes.as_mut_ptr();
unsafe { core::ptr::copy_nonoverlapping(src, dst, size) };
let drop_fn = |ptr| unsafe { core::ptr::drop_in_place(ptr as *mut T) };
core::mem::forget(value);
Some(Self {
type_id,
bytes,
drop_fn,
})
}
pub fn downcast_ref<T>(&self) -> Option<&T>
where
T: core::any::Any,
{
if core::any::TypeId::of::<T>() != self.type_id {
return None;
}
let ptr = self.bytes.as_ptr();
Some(unsafe { &*(ptr as *const T) })
}
pub fn downcast_mut<T>(&mut self) -> Option<&mut T>
where
T: core::any::Any,
{
if core::any::TypeId::of::<T>() != self.type_id {
return None;
}
let ptr = self.bytes.as_mut_ptr();
Some(unsafe { &mut *(ptr as *mut T) })
}
pub fn downcast<T>(mut self) -> Option<T>
where
T: core::any::Any,
{
if core::any::TypeId::of::<T>() != self.type_id {
return None;
}
self.drop_fn = |_| {};
let ptr = self.bytes.as_ptr();
Some(unsafe { core::ptr::read(ptr as *const T) })
}
}
impl<const N: usize> Drop for StackAny<N> {
fn drop(&mut self) {
(self.drop_fn)(self.bytes.as_mut_ptr());
}
}
#[macro_export]
macro_rules! stack_any {
($type:ty, $init:expr) => {
$crate::StackAny::<{ std::mem::size_of::<$type>() }>::try_new::<$type>($init).unwrap()
};
}