use crate::runtime::{ErasedArc, TypeMeta};
use std::mem;
use std::sync::Arc;
#[derive(Debug)]
pub enum Error {
MismatchedData,
MismatchedTypes,
}
#[derive(Clone, Debug)]
pub struct ErasedUnsizer {
type_in: TypeMeta,
type_out: TypeMeta,
typed_func: *const (),
erased_func: *const (),
}
unsafe impl Send for ErasedUnsizer {}
unsafe impl Sync for ErasedUnsizer {}
type TypedFunc<T, K> = fn(Arc<T>) -> Arc<K>;
type ErasedFunc<K> = fn(ErasedArc, typed: *const ()) -> Arc<K>;
impl ErasedUnsizer {
pub fn try_from<T, K>(func: TypedFunc<T, K>) -> Option<Self>
where
T: Send + Sync + 'static,
K: ?Sized + Send + Sync + 'static,
{
if size_of::<Arc<K>>() == size_of::<usize>() {
return None;
}
let erased: ErasedFunc<K> = |arc: ErasedArc, typed: *const ()| {
let arc = arc.coerce::<T>().unwrap();
let typed: TypedFunc<T, K> = unsafe { mem::transmute(typed) };
typed(arc)
};
Some(Self {
type_in: TypeMeta::of::<T>(),
type_out: TypeMeta::of::<K>(),
typed_func: func as *const (),
erased_func: erased as *const (),
})
}
pub fn type_in(&self) -> TypeMeta {
self.type_in
}
pub fn type_out(&self) -> TypeMeta {
self.type_out
}
pub fn unsize<K>(&self, arc: ErasedArc) -> Result<Arc<K>, Error>
where
K: ?Sized + Send + Sync + 'static,
{
if self.type_in != arc.ty() || self.type_out != TypeMeta::of::<K>() {
return Err(Error::MismatchedTypes);
}
let arc_ptr = ErasedArc::as_ptr(&arc);
let erased: ErasedFunc<K> = unsafe { mem::transmute(self.erased_func) };
let unsized_arc = erased(arc, self.typed_func);
if arc_ptr != Arc::as_ptr(&unsized_arc) as *const () {
return Err(Error::MismatchedData);
}
Ok(unsized_arc)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::any::Any;
#[test]
fn test_unsizes() {
let arc = ErasedArc::from_instance(10);
let unsizer =
ErasedUnsizer::try_from(|x: Arc<i32>| x as Arc<dyn Any + Send + Sync>).unwrap();
assert!(matches!(
unsizer.unsize::<dyn Any + Send + Sync>(arc),
Ok(..)
));
}
#[test]
fn test_fails_invalid_types() {
let arc = ErasedArc::from_instance(10i64);
let unsizer =
ErasedUnsizer::try_from(|x: Arc<i32>| x as Arc<dyn Any + Send + Sync>).unwrap();
assert!(matches!(
unsizer.unsize::<dyn Any + Send + Sync>(arc),
Err(Error::MismatchedTypes)
));
}
#[test]
fn test_fails_external_data() {
let arc = ErasedArc::from_instance(10);
let unsizer =
ErasedUnsizer::try_from(|x: Arc<i32>| Arc::new(1) as Arc<dyn Any + Send + Sync>)
.unwrap();
assert!(matches!(
unsizer.unsize::<dyn Any + Send + Sync>(arc),
Err(Error::MismatchedData)
));
}
#[test]
fn test_fails_to_sized() {
let arc = Arc::new(10);
let unsizer = ErasedUnsizer::try_from(|x: Arc<i32>| x);
assert!(matches!(unsizer, None));
}
}