1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
//! # stack-any
//!
//! A library that provides a type that owns same size type on the stack for type erasure.
//!
//! ## Usage
//!
//! ```
//! let mut stacks = [
//! stack_any::stack_any!(Vec<i32>, vec![]),
//! stack_any::stack_any!(Vec<char>, vec![]),
//! ];
//!
//! stacks[0].downcast_mut::<Vec<i32>>().unwrap().push(5);
//! stacks[1].downcast_mut::<Vec<char>>().unwrap().push('x');
//!
//! assert_eq!(stacks[0].downcast_ref(), Some(&vec![5]));
//! assert_eq!(stacks[1].downcast_ref(), Some(&vec!['x']));
//! ```
#![cfg_attr(not(feature = "std"), no_std)]
/// A convertible type that owns a stack allocation of `N` size.
#[derive(Debug)]
pub struct StackAny<const N: usize> {
type_id: core::any::TypeId,
bytes: [core::mem::MaybeUninit<u8>; N],
drop_fn: fn(*mut std::mem::MaybeUninit<u8>) -> (),
}
impl<const N: usize> StackAny<N> {
/// Allocates N-size memory on the stack and then places `value` into it.
/// Returns None if `T` size is larger than N.
///
/// # Examples
///
/// ```
/// let five = stack_any::StackAny::<{ std::mem::size_of::<i32>() }>::try_new(5);
/// ```
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,
})
}
/// Attempt to return reference to the inner value as a concrete type.
/// Returns None if `T` is not equal to contained value type.
///
/// # Examples
///
/// ```
/// let five = stack_any::stack_any!(i32, 5);
/// assert_eq!(five.downcast_ref::<i32>(), Some(&5));
/// assert_eq!(five.downcast_ref::<i64>(), None);
/// ```
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) })
}
/// Attempt to return mutable reference to the inner value as a concrete type.
/// Returns None if `T` is not equal to contained value type.
///
/// # Examples
///
/// ```
/// let mut five = stack_any::stack_any!(i32, 5);
/// assert_eq!(five.downcast_mut::<i32>(), Some(&mut 5));
/// assert_eq!(five.downcast_mut::<i64>(), None);
/// ```
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) })
}
/// Attempt to downcast the stack to a concrete type.
/// Returns None if `T` is not equal to contained value type.
///
/// # Examples
///
/// ```
/// let five = stack_any::stack_any!(i32, 5);
/// assert_eq!(five.downcast::<i32>(), Some(5));
/// ```
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());
}
}
/// Allocates memory on the stack and then places value based on given type and value.
///
/// # Examples
///
/// ```
/// let five = stack_any::stack_any!(i32, 5);
/// ```
#[macro_export]
macro_rules! stack_any {
($type:ty, $init:expr) => {
$crate::StackAny::<{ std::mem::size_of::<$type>() }>::try_new::<$type>($init).unwrap()
};
}