#![no_std]
#![feature(ptr_metadata, unsize, arbitrary_self_types)]
#![warn(missing_docs)]
extern crate alloc;
use alloc::boxed::Box;
use core::{
alloc::Layout,
any::{Any, TypeId},
fmt,
hash::Hash,
marker::{PhantomData, Unsize},
ptr::{DynMetadata, Pointee},
sync::atomic::{AtomicPtr, Ordering},
};
use hashbrown::{HashMap, HashSet};
use hv_atom::AtomSetOnce;
use hv_cell::AtomicRefCell;
use lazy_static::lazy_static;
use spin::RwLock;
pub trait WithSend<T: ?Sized> {
fn with_send(&mut self)
where
T: Send;
}
pub trait WithSync<T: ?Sized> {
fn with_sync(&mut self)
where
T: Sync;
}
pub trait WithCopy<T: ?Sized> {
fn with_copy(&mut self)
where
T: Copy;
}
pub trait WithClone<T: ?Sized> {
fn with_clone(&mut self)
where
T: Clone;
}
pub fn of<T: 'static>() -> Type<T> {
Type::of()
}
pub fn convertible<T: 'static, U: 'static + From<T>>() {
of::<T>().add_into::<U>();
of::<U>().add_from::<T>();
}
fn typed<T: 'static>() -> Type<T> {
Type::new()
}
macro_rules! add_types {
($m:ident, $closure:expr; $($t:ty),*) => {{
$({
let t = <Type<$t>>::new();
$closure(t);
$m.insert(TypeId::of::<$t>(), t.as_untyped());
})*
}}
}
fn make_registry() -> HashMap<TypeId, &'static TypeTable> {
fn smart_pointers<T: 'static>(_: Type<T>) {
use alloc::{
rc::{Rc, Weak as RcWeak},
sync::{Arc, Weak as ArcWeak},
};
typed::<Rc<T>>().add_clone();
typed::<RcWeak<T>>().add_clone();
typed::<Arc<T>>().add_clone();
typed::<ArcWeak<T>>().add_clone();
typed::<&'static T>().add_clone().add_copy();
}
fn wrappers<T: 'static>(_: Type<T>) {
smart_pointers::<T>(typed());
smart_pointers::<core::cell::RefCell<T>>(typed());
smart_pointers::<AtomicRefCell<T>>(typed());
}
fn primitive<T: 'static>(t: Type<T>)
where
T: Clone
+ Copy
+ PartialEq
+ Eq
+ PartialOrd
+ Ord
+ Hash
+ fmt::Debug
+ fmt::Display
+ Send
+ Sync,
{
t.add_clone().add_copy().add_send().add_sync();
wrappers(t);
}
let mut m = HashMap::new();
add_types! {m,
primitive;
u8, u16, u32, u64, u128, usize,
i8, i16, i32, i64, i128, isize,
&'static str
};
m
}
lazy_static! {
static ref ALCHEMY_TABLE_REGISTRY: RwLock<HashMap<TypeId, &'static TypeTable>> =
RwLock::new(make_registry());
static ref VALID_ALCHEMY_TABLES: RwLock<HashSet<usize>> = RwLock::new(HashSet::new());
}
pub trait CloneProxy {
#[doc(hidden)]
unsafe fn clone_into_ptr(&self, ptr: *mut u8);
#[doc(hidden)]
unsafe fn clone_fn(self: *const Self) -> fn();
}
impl<T: Clone> CloneProxy for T {
unsafe fn clone_into_ptr(&self, ptr: *mut u8) {
(&mut *ptr.cast::<T>()).clone_from(self);
}
unsafe fn clone_fn(self: *const T) -> fn() {
core::mem::transmute(<T as Clone>::clone as fn(&T) -> T)
}
}
pub trait CopyProxy {
#[doc(hidden)]
unsafe fn copy_into_ptr(&self, ptr: *mut u8);
}
impl<T: Copy> CopyProxy for T {
unsafe fn copy_into_ptr(&self, ptr: *mut u8) {
*ptr.cast::<T>() = *self;
}
}
static_assertions::assert_obj_safe!(CloneProxy, CopyProxy);
pub trait FromProxy<T> {
unsafe fn convert_from_ptr(self: *mut Self, ptr: *const T);
}
impl<T: From<U>, U> FromProxy<U> for T {
unsafe fn convert_from_ptr(self: *mut Self, ptr: *const U) {
core::ptr::write(self, T::from(core::ptr::read(ptr.cast::<U>())));
}
}
pub trait IntoProxy<T> {
unsafe fn convert_into_ptr(self: *const Self, ptr: *mut T);
}
impl<T: Into<U>, U> IntoProxy<U> for T {
unsafe fn convert_into_ptr(self: *const Self, ptr: *mut U) {
core::ptr::write(ptr.cast::<U>(), T::into(core::ptr::read(self)));
}
}
static_assertions::assert_obj_safe!(FromProxy<()>);
pub trait Alchemical<U: ?Sized + Alchemy>: Any {
#[doc(hidden)]
fn cast_ptr(this: *const Self) -> *const U;
#[doc(hidden)]
fn cast_mut_ptr(this: *mut Self) -> *mut U;
}
impl<T: ?Sized + Any + Unsize<U>, U: ?Sized + Alchemy> Alchemical<U> for T {
#[inline]
fn cast_ptr(this: *const Self) -> *const U {
this as *const U
}
#[inline]
fn cast_mut_ptr(this: *mut Self) -> *mut U {
this as *mut U
}
}
pub trait Alchemy: Any + Pointee<Metadata = DynMetadata<Self>> {}
impl<T> Alchemy for T where T: ?Sized + Any + Pointee<Metadata = DynMetadata<T>> {}
static_assertions::assert_impl_all!(dyn Any: Alchemy);
static_assertions::assert_impl_all!((): CloneProxy);
pub struct TypeTable {
pub id: TypeId,
pub layout: Layout,
pub drop: unsafe fn(*mut u8),
pub type_name: &'static str,
vtables: RwLock<HashMap<TypeId, &'static DynVtable>>,
pub(crate) alchemical_any: DynVtable,
pub(crate) alchemical_clone: AtomSetOnce<&'static DynVtable>,
pub(crate) alchemical_copy: AtomSetOnce<&'static DynVtable>,
pub(crate) send: AtomSetOnce<&'static DynVtable>,
pub(crate) sync: AtomSetOnce<&'static DynVtable>,
pub(crate) send_shim: AtomicPtr<()>,
pub(crate) sync_shim: AtomicPtr<()>,
pub(crate) clone_shim: AtomicPtr<()>,
pub(crate) copy_shim: AtomicPtr<()>,
}
impl TypeTable {
fn new<T: 'static>() -> &'static Self {
unsafe fn drop_ptr<T>(x: *mut u8) {
x.cast::<T>().drop_in_place()
}
let this = Self {
id: TypeId::of::<T>(),
layout: Layout::new::<T>(),
drop: drop_ptr::<T>,
type_name: core::any::type_name::<T>(),
vtables: RwLock::new(HashMap::new()),
alchemical_any: DynVtable::new::<T, dyn AlchemicalAny>(core::ptr::null()),
alchemical_clone: AtomSetOnce::empty(),
alchemical_copy: AtomSetOnce::empty(),
send: AtomSetOnce::empty(),
sync: AtomSetOnce::empty(),
send_shim: AtomicPtr::new(core::ptr::null_mut()),
sync_shim: AtomicPtr::new(core::ptr::null_mut()),
clone_shim: AtomicPtr::new(core::ptr::null_mut()),
copy_shim: AtomicPtr::new(core::ptr::null_mut()),
};
Box::leak(Box::new(this))
}
pub fn of<T: 'static>() -> &'static Self {
let type_id = TypeId::of::<T>();
if let Some(table) = ALCHEMY_TABLE_REGISTRY.read().get(&type_id).copied() {
return table;
}
ALCHEMY_TABLE_REGISTRY
.write()
.entry(type_id)
.or_insert_with(|| {
let eternal = Self::new::<T>();
VALID_ALCHEMY_TABLES
.write()
.insert(eternal as *const _ as usize);
eternal
})
}
pub fn is_clone(&self) -> bool {
!self.alchemical_clone.is_none()
}
pub fn is_copy(&self) -> bool {
!self.alchemical_copy.is_none()
}
pub fn is<U: ?Sized + Alchemy>(&self) -> bool {
self.get::<U>().is_some()
}
pub fn get<U>(&self) -> Option<&DynVtable>
where
U: ?Sized + Alchemy,
{
let id = TypeId::of::<U>();
if id == TypeId::of::<dyn AlchemicalAny>() {
return Some(&self.alchemical_any);
} else if id == TypeId::of::<dyn CloneProxy>() {
return self.alchemical_clone.get();
} else if id == TypeId::of::<dyn CopyProxy>() {
return self.alchemical_copy.get();
} else if id == TypeId::of::<dyn Send>() {
return self.send.get();
} else if id == TypeId::of::<dyn Sync>() {
return self.sync.get();
}
self.vtables.read().get(&id).copied()
}
pub fn get_or_insert<T, U>(&self, ptr: *const T) -> &DynVtable
where
T: ?Sized + Alchemical<U>,
U: ?Sized + Alchemy,
{
assert_eq!(TypeId::of::<T>(), self.id);
match self.get::<U>() {
Some(table) => table,
None => {
let id = TypeId::of::<U>();
let vtable = Box::leak(Box::new(DynVtable::new::<T, U>(ptr)));
if id == TypeId::of::<dyn CloneProxy>() {
panic!("use `add_clone` instead of explicitly adding `dyn CloneProxy`!");
} else if id == TypeId::of::<dyn CopyProxy>() {
panic!("use `add_copy` instead of explicitly adding `dyn CopyProxy`!");
} else if id == TypeId::of::<dyn Send>() {
panic!("use `add_send` instead of explicitly adding `dyn Send`!");
} else if id == TypeId::of::<dyn Sync>() {
panic!("use `add_sync` instead of explicitly adding `dyn Sync`!");
} else {
self.vtables.write().insert(id, vtable);
}
vtable
}
}
}
pub fn get_or_insert_sized<T, U>(&self) -> &DynVtable
where
T: Alchemical<U>,
U: ?Sized + Alchemy,
{
self.get_or_insert::<T, U>(core::ptr::null())
}
pub fn add_clone<T: Clone + 'static>(&self) -> &Self {
fn clone_shim<T: Clone>(with: &mut dyn WithClone<T>) {
with.with_clone();
}
assert_eq!(TypeId::of::<T>(), self.id);
self.alchemical_clone.set_if_none(Box::leak(Box::new(
DynVtable::new::<T, dyn CloneProxy>(core::ptr::null()),
)));
let shim_ptr = clone_shim::<T> as *const () as *mut ();
let _ = self.clone_shim.compare_exchange(
core::ptr::null_mut(),
shim_ptr,
Ordering::AcqRel,
Ordering::Acquire,
);
self
}
pub fn add_copy<T: Copy + 'static>(&self) -> &Self {
fn copy_shim<T: Copy>(with: &mut dyn WithCopy<T>) {
with.with_copy();
}
assert_eq!(TypeId::of::<T>(), self.id);
self.alchemical_copy
.set_if_none(Box::leak(Box::new(DynVtable::new::<T, dyn CopyProxy>(
core::ptr::null(),
))));
let shim_ptr = copy_shim::<T> as *const () as *mut ();
let _ = self.copy_shim.compare_exchange(
core::ptr::null_mut(),
shim_ptr,
Ordering::AcqRel,
Ordering::Acquire,
);
self
}
pub fn add_send<T: Send + 'static>(&self) -> &Self {
self.add_send_with::<T>(core::ptr::null())
}
pub fn add_sync<T: Sync + 'static>(&self) -> &Self {
self.add_sync_with::<T>(core::ptr::null())
}
pub fn add_send_with<T>(&self, ptr: *const T) -> &Self
where
T: ?Sized + Send + Alchemical<dyn Send> + 'static,
{
fn send_shim<T: ?Sized + Send>(with: &mut dyn WithSend<T>) {
with.with_send();
}
assert_eq!(TypeId::of::<T>(), self.id);
self.send
.set_if_none(Box::leak(Box::new(DynVtable::new::<T, dyn Send>(ptr))));
let shim_ptr = send_shim::<T> as *const () as *mut ();
let _ = self.send_shim.compare_exchange(
core::ptr::null_mut(),
shim_ptr,
Ordering::AcqRel,
Ordering::Acquire,
);
self
}
pub fn add_sync_with<T>(&self, ptr: *const T) -> &Self
where
T: ?Sized + Sync + Alchemical<dyn Sync> + 'static,
{
fn sync_shim<T: ?Sized + Sync>(with: &mut dyn WithSync<T>) {
with.with_sync();
}
assert_eq!(TypeId::of::<T>(), self.id);
self.sync
.set_if_none(Box::leak(Box::new(DynVtable::new::<T, dyn Sync>(ptr))));
let shim_ptr = sync_shim::<T> as *const () as *mut ();
let _ = self.sync_shim.compare_exchange(
core::ptr::null_mut(),
shim_ptr,
Ordering::AcqRel,
Ordering::Acquire,
);
self
}
pub fn add<T, U>(&self) -> &Self
where
T: Alchemical<U>,
U: ?Sized + Alchemy,
{
self.get_or_insert::<T, U>(core::ptr::null());
self
}
pub fn add_with<T, U>(&self, ptr: *const T) -> &Self
where
T: ?Sized + Alchemical<U>,
U: ?Sized + Alchemy,
{
self.get_or_insert::<T, U>(ptr);
self
}
pub fn to_ptr(&'static self) -> *const TypeTable {
self
}
pub fn from_ptr(ptr: *const TypeTable) -> Option<&'static TypeTable> {
VALID_ALCHEMY_TABLES
.read()
.contains(&(ptr as usize))
.then(|| unsafe { &*ptr })
}
}
pub struct Type<T: ?Sized>(&'static TypeTable, PhantomData<fn(T)>);
impl<T: ?Sized> Clone for Type<T> {
fn clone(&self) -> Self {
Self(self.0, self.1)
}
}
impl<T: ?Sized> Copy for Type<T> {}
impl<T: ?Sized + 'static> Type<T> {
fn new() -> Self
where
T: Sized,
{
Self(TypeTable::new::<T>(), PhantomData)
}
pub fn of() -> Self
where
T: Sized,
{
Self(TypeTable::of::<T>(), PhantomData)
}
pub fn is<U: ?Sized + Alchemy>(&self) -> bool {
self.0.is::<U>()
}
pub fn get<U>(self) -> Option<&'static DynVtable>
where
U: ?Sized + Alchemy,
{
self.0.get::<U>()
}
pub fn get_or_insert<U>(self, ptr: *const T) -> &'static DynVtable
where
T: Alchemical<U>,
U: ?Sized + Alchemy,
{
self.0.get_or_insert::<T, U>(ptr)
}
pub fn get_or_insert_sized<U>(self) -> &'static DynVtable
where
T: Sized + Alchemical<U>,
U: ?Sized + Alchemy,
{
self.0.get_or_insert_sized::<T, U>()
}
pub fn add_clone(self) -> Self
where
T: Clone,
{
self.0.add_clone::<T>();
self
}
pub fn add_copy(self) -> Self
where
T: Copy,
{
self.0.add_copy::<T>();
self
}
pub fn add_send(self) -> Self
where
T: Send + Sized,
{
self.0.add_send::<T>();
self
}
pub fn add_sync(self) -> Self
where
T: Sync + Sized,
{
self.0.add_sync::<T>();
self
}
pub fn add_with<U>(self, ptr: *const T) -> Self
where
T: Alchemical<U>,
U: ?Sized + Alchemy,
{
self.0.add_with::<T, U>(ptr);
self
}
pub fn add<U>(self) -> Self
where
T: Sized + Alchemical<U>,
U: ?Sized + Alchemy,
{
self.0.add::<T, U>();
self
}
pub fn as_untyped(self) -> &'static TypeTable {
self.0
}
pub fn try_prove_copy(self, with: &mut dyn WithCopy<T>) {
let ptr = self.0.copy_shim.load(Ordering::Acquire);
if !ptr.is_null() {
let f = unsafe { core::mem::transmute::<*mut (), fn(&mut dyn WithCopy<T>)>(ptr) };
f(with);
}
}
pub fn try_prove_clone(self, with: &mut dyn WithClone<T>) {
let ptr = self.0.clone_shim.load(Ordering::Acquire);
if !ptr.is_null() {
let f = unsafe { core::mem::transmute::<*mut (), fn(&mut dyn WithClone<T>)>(ptr) };
f(with);
}
}
pub fn try_prove_send(self, with: &mut dyn WithSend<T>) {
let ptr = self.0.send_shim.load(Ordering::Acquire);
if !ptr.is_null() {
let f = unsafe { core::mem::transmute::<*mut (), fn(&mut dyn WithSend<T>)>(ptr) };
f(with);
}
}
pub fn try_prove_sync(self, with: &mut dyn WithSync<T>) {
let ptr = self.0.sync_shim.load(Ordering::Acquire);
if !ptr.is_null() {
let f = unsafe { core::mem::transmute::<*mut (), fn(&mut dyn WithSync<T>)>(ptr) };
f(with);
}
}
}
impl<T: 'static> Type<T> {
pub fn add_from<U: 'static>(self) -> Self
where
T: From<U>,
{
self.add::<dyn FromProxy<U>>()
}
pub fn add_into<U: 'static>(self) -> Self
where
T: Into<U>,
{
self.add::<dyn IntoProxy<U>>()
}
pub fn add_conversion_into<U: 'static>(self) -> Self
where
U: From<T>,
{
of::<U>().add_from::<T>();
self.add_into::<U>()
}
pub fn add_conversion_from<U: 'static>(self) -> Self
where
T: From<U>,
{
of::<U>().add_into::<T>();
self.add_from::<U>()
}
pub fn get_clone(self) -> Option<fn(&T) -> T> {
let vtable = self.get::<dyn CloneProxy>()?;
let clone_fn = unsafe {
let null_obj_ptr = vtable.to_dyn_object_ptr::<dyn CloneProxy>(core::ptr::null());
let untyped_clone_fn: fn() = CloneProxy::clone_fn(null_obj_ptr);
core::mem::transmute::<_, fn(&T) -> T>(untyped_clone_fn)
};
Some(clone_fn)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DynVtable {
obj_type_id: TypeId,
dyn_type_id: TypeId,
metadata: *const (),
}
unsafe impl Send for DynVtable {}
unsafe impl Sync for DynVtable {}
impl DynVtable {
pub fn new<T, U>(ptr: *const T) -> Self
where
T: ?Sized + Alchemical<U>,
U: ?Sized + Alchemy,
{
let cast_ptr = <T as Alchemical<U>>::cast_ptr(ptr);
Self {
obj_type_id: TypeId::of::<T>(),
dyn_type_id: TypeId::of::<U>(),
metadata: unsafe {
let u_metadata = core::ptr::metadata::<U>(cast_ptr);
core::mem::transmute::<DynMetadata<U>, *const ()>(u_metadata)
},
}
}
pub unsafe fn new_from_parts(
obj_type_id: TypeId,
dyn_type_id: TypeId,
metadata: *const (),
) -> Self {
Self {
obj_type_id,
dyn_type_id,
metadata,
}
}
pub fn obj_type_id(&self) -> TypeId {
self.obj_type_id
}
pub fn dyn_type_id(&self) -> TypeId {
self.dyn_type_id
}
pub unsafe fn to_dyn_object_ptr<U: Any + ?Sized + Pointee<Metadata = DynMetadata<U>>>(
&self,
object: *const (),
) -> *const U {
core::ptr::from_raw_parts::<U>(
object,
core::mem::transmute::<*const (), DynMetadata<U>>(self.metadata),
)
}
pub unsafe fn to_dyn_object_mut_ptr<U: Any + ?Sized + Pointee<Metadata = DynMetadata<U>>>(
&self,
object: *mut (),
) -> *mut U {
core::ptr::from_raw_parts_mut::<U>(
object,
core::mem::transmute::<*const (), DynMetadata<U>>(self.metadata),
)
}
}
#[derive(Clone, Copy)]
pub struct AlchemicalPtr {
data: *mut (),
table: &'static TypeTable,
}
impl AlchemicalPtr {
pub fn new<T: Any>(ptr: *mut T) -> Self {
Self {
data: ptr.cast(),
table: TypeTable::of::<T>(),
}
}
pub fn as_ptr(self) -> *mut () {
self.data
}
pub fn table(self) -> &'static TypeTable {
self.table
}
pub unsafe fn as_alchemical_any(self) -> *const dyn AlchemicalAny {
self.table.alchemical_any.to_dyn_object_ptr(self.data)
}
pub unsafe fn as_alchemical_any_mut(self) -> *mut dyn AlchemicalAny {
self.table.alchemical_any.to_dyn_object_mut_ptr(self.data)
}
pub unsafe fn from_raw_parts(data: *mut (), table: &'static TypeTable) -> Self {
Self { data, table }
}
pub fn downcast_dyn_ptr<U: ?Sized + Alchemy>(self) -> Option<*const U> {
self.table
.get::<U>()
.map(|vtable| unsafe { vtable.to_dyn_object_ptr::<U>(self.data.cast()) })
}
pub fn downcast_dyn_mut_ptr<U: ?Sized + Alchemy>(self) -> Option<*mut U> {
self.table
.get::<U>()
.map(|vtable| unsafe { vtable.to_dyn_object_mut_ptr::<U>(self.data.cast()) })
}
pub unsafe fn downcast_dyn_ref<'a, U: ?Sized + Alchemy>(self) -> Option<&'a U> {
self.table
.get::<U>()
.map(|vtable| &*unsafe { vtable.to_dyn_object_ptr::<U>(self.data.cast()) })
}
pub unsafe fn downcast_dyn_mut<'a, U: ?Sized + Alchemy>(self) -> Option<&'a mut U> {
self.table
.get::<U>()
.map(|vtable| &mut *unsafe { vtable.to_dyn_object_mut_ptr::<U>(self.data.cast()) })
}
}
pub trait AlchemicalAny {
fn type_table(&self) -> &'static TypeTable;
}
impl<T: Any> AlchemicalAny for T {
fn type_table(&self) -> &'static TypeTable {
TypeTable::of::<T>()
}
}
pub trait AlchemicalAnyExt: AlchemicalAny {
fn dyncast_ref<U: Alchemy + ?Sized>(&self) -> Option<&U> {
let type_table = (*self).type_table();
let downcast_alchemy = type_table.get::<U>()?;
unsafe { Some(&*downcast_alchemy.to_dyn_object_ptr::<U>((self as *const Self).cast())) }
}
fn dyncast_mut<U: Alchemy + ?Sized>(&mut self) -> Option<&mut U> {
let type_table = (*self).type_table();
let downcast_alchemy = type_table.get::<U>()?;
unsafe {
Some(&mut *downcast_alchemy.to_dyn_object_mut_ptr::<U>((self as *mut Self).cast()))
}
}
fn dyncast<U: Alchemy + ?Sized>(self: Box<Self>) -> Option<Box<U>> {
let type_table = (*self).type_table();
let downcast_alchemy = type_table.get::<U>()?;
unsafe {
let ptr = Box::into_raw(self);
Some(Box::from_raw(
downcast_alchemy.to_dyn_object_mut_ptr::<U>(ptr as *mut _),
))
}
}
fn downcast_ref<T: Any>(&self) -> Option<&T> {
let tt = (*self).type_table();
(tt.id == TypeId::of::<T>()).then(|| unsafe { &*(self as *const _ as *const T) })
}
fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
let tt = (*self).type_table();
(tt.id == TypeId::of::<T>()).then(|| unsafe { &mut *(self as *mut _ as *mut T) })
}
fn downcast<T: Any>(self: Box<Self>) -> Option<Box<T>> {
let tt = (*self).type_table();
(tt.id == TypeId::of::<T>())
.then(|| unsafe { Box::from_raw(Box::into_raw(self) as *mut T) })
}
fn try_copy(&self) -> Option<Box<dyn AlchemicalAny>> {
let at = self.type_table();
let as_alchemical_copy = at.get::<dyn CopyProxy>()?;
unsafe {
let ptr = alloc::alloc::alloc(at.layout);
(*as_alchemical_copy.to_dyn_object_ptr::<dyn CopyProxy>((self as *const Self).cast()))
.copy_into_ptr(ptr);
let recast_ptr = at
.alchemical_any
.to_dyn_object_mut_ptr::<dyn AlchemicalAny>(ptr as *mut _);
Some(Box::from_raw(recast_ptr))
}
}
fn try_clone(&self) -> Option<Box<dyn AlchemicalAny>> {
let at = self.type_table();
let as_alchemical_clone = at.get::<dyn CloneProxy>()?;
unsafe {
let ptr = alloc::alloc::alloc(at.layout);
(*as_alchemical_clone
.to_dyn_object_ptr::<dyn CloneProxy>((self as *const Self).cast()))
.clone_into_ptr(ptr);
let recast_ptr = at
.alchemical_any
.to_dyn_object_mut_ptr::<dyn AlchemicalAny>(ptr as *mut _);
Some(Box::from_raw(recast_ptr))
}
}
}
impl<T: AlchemicalAny + ?Sized> AlchemicalAnyExt for T {}
pub unsafe fn clone_or_move(src: *mut dyn AlchemicalAny, dst: *mut u8) -> bool {
let table = <dyn AlchemicalAny>::type_table(&*src);
if let Some(clone_vt) = table.get::<dyn CloneProxy>() {
clone_vt
.to_dyn_object_ptr::<dyn CloneProxy>(src as *mut ())
.clone_into_ptr(dst as *mut u8);
false
} else {
core::ptr::copy_nonoverlapping(src as *const u8, dst as *mut u8, table.layout.size());
true
}
}
pub unsafe fn copy_clone_or_move_to(
src: *mut dyn AlchemicalAny,
dst: *mut dyn AlchemicalAny,
) -> bool {
let table = <dyn AlchemicalAny>::type_table(&*src);
assert!(core::ptr::eq(table, <dyn AlchemicalAny>::type_table(&*dst)));
if let Some(copy_vt) = table.get::<dyn CopyProxy>() {
copy_vt
.to_dyn_object_ptr::<dyn CopyProxy>(src as *mut ())
.copy_into_ptr(dst as *mut u8);
false
} else if let Some(clone_vt) = table.get::<dyn CloneProxy>() {
clone_vt
.to_dyn_object_ptr::<dyn CloneProxy>(src as *mut ())
.clone_into_ptr(dst as *mut u8);
false
} else {
core::ptr::copy_nonoverlapping(src as *const u8, dst as *mut u8, table.layout.size());
true
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn alchemical_clone() {
Type::<i32>::of().add_clone();
let boxed: Box<dyn AlchemicalAny> = Box::new(5i32);
let other: Box<dyn AlchemicalAny> = (*boxed).try_clone().unwrap();
let a = *(*boxed).downcast_ref::<i32>().unwrap();
let b = *(*other).downcast_ref::<i32>().unwrap();
assert_eq!(a, b);
}
#[test]
fn alchemical_copy() {
Type::<i32>::of().add_copy();
let boxed: Box<dyn AlchemicalAny> = Box::new(5i32);
let other: Box<dyn AlchemicalAny> = (*boxed).try_copy().unwrap();
let a = *(*boxed).downcast_ref::<i32>().unwrap();
let b = *(*other).downcast_ref::<i32>().unwrap();
assert_eq!(a, b);
}
}