#![warn(missing_docs)]
#![no_std]
extern crate alloc;
#[doc(no_inline)]
pub use const_field_offset::*;
use core::marker::PhantomData;
use core::ops::{Deref, DerefMut, Drop};
use core::{pin::Pin, ptr::NonNull};
#[doc(inline)]
pub use vtable_macro::*;
mod vrc;
pub use vrc::*;
pub unsafe trait VTableMeta {
type Target;
type VTable: 'static;
}
pub unsafe trait VTableMetaDrop: VTableMeta {
unsafe fn drop(ptr: *mut Self::Target);
fn new_box<X: HasStaticVTable<Self>>(value: X) -> VBox<Self>;
}
pub unsafe trait HasStaticVTable<VT>
where
VT: ?Sized + VTableMeta,
{
fn static_vtable() -> &'static VT::VTable;
}
#[derive(Copy, Clone)]
#[allow(dead_code)]
#[repr(C)]
struct Inner {
vtable: NonNull<u8>,
ptr: NonNull<u8>,
}
impl Inner {
fn deref<T: ?Sized + VTableMeta>(&self) -> *const T::Target {
debug_assert_eq!(core::mem::size_of::<T::Target>(), core::mem::size_of::<Inner>());
self as *const Inner as *const T::Target
}
fn deref_mut<T: ?Sized + VTableMeta>(&mut self) -> *mut T::Target {
debug_assert_eq!(core::mem::size_of::<T::Target>(), core::mem::size_of::<Inner>());
self as *mut Inner as *mut T::Target
}
}
#[repr(transparent)]
pub struct VBox<T: ?Sized + VTableMetaDrop> {
inner: Inner,
phantom: PhantomData<T::Target>,
}
impl<T: ?Sized + VTableMetaDrop> Deref for VBox<T> {
type Target = T::Target;
fn deref(&self) -> &Self::Target {
unsafe { &*self.inner.deref::<T>() }
}
}
impl<T: ?Sized + VTableMetaDrop> DerefMut for VBox<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *(self.inner.deref_mut::<T>() as *mut _) }
}
}
impl<T: ?Sized + VTableMetaDrop> Drop for VBox<T> {
fn drop(&mut self) {
unsafe {
T::drop(self.inner.deref::<T>() as *mut _);
}
}
}
impl<T: ?Sized + VTableMetaDrop> VBox<T> {
pub fn new<X: HasStaticVTable<T>>(value: X) -> Self {
T::new_box(value)
}
pub unsafe fn from_raw(vtable: NonNull<T::VTable>, ptr: NonNull<u8>) -> Self {
Self { inner: Inner { vtable: vtable.cast(), ptr }, phantom: PhantomData }
}
pub fn borrow(&self) -> VRef<'_, T> {
unsafe { VRef::from_inner(self.inner) }
}
pub fn borrow_mut(&mut self) -> VRefMut<'_, T> {
unsafe { VRefMut::from_inner(self.inner) }
}
pub fn leak(self) -> VRefMut<'static, T> {
let inner = self.inner;
core::mem::forget(self);
unsafe { VRefMut::from_inner(inner) }
}
}
#[repr(transparent)]
pub struct VRef<'a, T: ?Sized + VTableMeta> {
inner: Inner,
phantom: PhantomData<&'a T::Target>,
}
impl<T: ?Sized + VTableMeta> Copy for VRef<'_, T> {}
impl<T: ?Sized + VTableMeta> Clone for VRef<'_, T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized + VTableMeta> Deref for VRef<'_, T> {
type Target = T::Target;
fn deref(&self) -> &Self::Target {
unsafe { &*self.inner.deref::<T>() }
}
}
impl<'a, T: ?Sized + VTableMeta> VRef<'a, T> {
pub fn new<X: HasStaticVTable<T>>(value: &'a X) -> Self {
Self {
inner: Inner {
vtable: NonNull::from(X::static_vtable()).cast(),
ptr: NonNull::from(value).cast(),
},
phantom: PhantomData,
}
}
pub fn new_pin<X: HasStaticVTable<T>>(value: core::pin::Pin<&'a X>) -> Pin<Self> {
unsafe {
Pin::new_unchecked(Self {
inner: Inner {
vtable: NonNull::from(X::static_vtable()).cast(),
ptr: NonNull::from(value.get_ref()).cast(),
},
phantom: PhantomData,
})
}
}
unsafe fn from_inner(inner: Inner) -> Self {
Self { inner, phantom: PhantomData }
}
pub unsafe fn from_raw(vtable: NonNull<T::VTable>, ptr: NonNull<u8>) -> Self {
Self { inner: Inner { vtable: vtable.cast(), ptr }, phantom: PhantomData }
}
pub fn downcast<X: HasStaticVTable<T>>(&self) -> Option<&X> {
if self.inner.vtable == NonNull::from(X::static_vtable()).cast() {
unsafe { Some(self.inner.ptr.cast().as_ref()) }
} else {
None
}
}
pub fn downcast_pin<X: HasStaticVTable<T>>(this: Pin<Self>) -> Option<Pin<&'a X>> {
let inner = unsafe { Pin::into_inner_unchecked(this).inner };
if inner.vtable == NonNull::from(X::static_vtable()).cast() {
unsafe { Some(Pin::new_unchecked(inner.ptr.cast().as_ref())) }
} else {
None
}
}
pub fn as_ptr(this: Self) -> NonNull<u8> {
this.inner.ptr
}
}
#[repr(transparent)]
pub struct VRefMut<'a, T: ?Sized + VTableMeta> {
inner: Inner,
phantom: PhantomData<&'a mut T::Target>,
}
impl<T: ?Sized + VTableMeta> Deref for VRefMut<'_, T> {
type Target = T::Target;
fn deref(&self) -> &Self::Target {
unsafe { &*self.inner.deref::<T>() }
}
}
impl<T: ?Sized + VTableMeta> DerefMut for VRefMut<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *(self.inner.deref_mut::<T>() as *mut _) }
}
}
impl<'a, T: ?Sized + VTableMeta> VRefMut<'a, T> {
pub fn new<X: HasStaticVTable<T>>(value: &'a mut X) -> Self {
Self {
inner: Inner {
vtable: NonNull::from(X::static_vtable()).cast(),
ptr: NonNull::from(value).cast(),
},
phantom: PhantomData,
}
}
unsafe fn from_inner(inner: Inner) -> Self {
Self { inner, phantom: PhantomData }
}
pub unsafe fn from_raw(vtable: NonNull<T::VTable>, ptr: NonNull<u8>) -> Self {
Self { inner: Inner { vtable: vtable.cast(), ptr }, phantom: PhantomData }
}
pub fn borrow(&self) -> VRef<'_, T> {
unsafe { VRef::from_inner(self.inner) }
}
pub fn borrow_mut(&mut self) -> VRefMut<'_, T> {
unsafe { VRefMut::from_inner(self.inner) }
}
pub fn into_ref(self) -> VRef<'a, T> {
unsafe { VRef::from_inner(self.inner) }
}
pub fn downcast<X: HasStaticVTable<T>>(&mut self) -> Option<&mut X> {
if self.inner.vtable == NonNull::from(X::static_vtable()).cast() {
unsafe { Some(self.inner.ptr.cast().as_mut()) }
} else {
None
}
}
}
#[macro_export]
macro_rules! new_vref {
(let $ident:ident : VRef<$vtable:ty> for $trait_:path = $e:expr) => {
let vtable = {
use $crate::VTableMeta;
fn get_vt<X: $trait_>(_: &X) -> <$vtable as VTableMeta>::VTable {
<$vtable as VTableMeta>::VTable::new::<X>()
}
get_vt($e)
};
let $ident = {
use $crate::VTableMeta;
fn create<'a, X: $trait_>(
vtable: &'a <$vtable as VTableMeta>::VTable,
val: &'a X,
) -> $crate::VRef<'a, <$vtable as VTableMeta>::VTable> {
use ::core::ptr::NonNull;
unsafe { $crate::VRef::from_raw(NonNull::from(vtable), NonNull::from(val).cast()) }
}
create(&vtable, $e)
};
};
(let mut $ident:ident : VRefMut<$vtable:ty> for $trait_:path = $e:expr) => {
let vtable = {
use $crate::VTableMeta;
fn get_vt<X: $trait_>(_: &mut X) -> <$vtable as VTableMeta>::VTable {
<$vtable as VTableMeta>::VTable::new::<X>()
}
get_vt($e)
};
let mut $ident = {
use $crate::VTableMeta;
fn create<'a, X: $trait_>(
vtable: &'a <$vtable as VTableMeta>::VTable,
val: &'a mut X,
) -> $crate::VRefMut<'a, <$vtable as VTableMeta>::VTable> {
use ::core::ptr::NonNull;
unsafe {
$crate::VRefMut::from_raw(NonNull::from(vtable), NonNull::from(val).cast())
}
}
create(&vtable, $e)
};
};
}
#[repr(C)]
pub struct VOffset<Base, T: ?Sized + VTableMeta, PinFlag = NotPinned> {
vtable: &'static T::VTable,
offset: usize,
phantom: PhantomData<FieldOffset<Base, (), PinFlag>>,
}
impl<Base, T: ?Sized + VTableMeta, PinFlag> core::fmt::Debug for VOffset<Base, T, PinFlag> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "VOffset({})", self.offset)
}
}
impl<Base, T: ?Sized + VTableMeta, Flag> VOffset<Base, T, Flag> {
#[inline]
pub fn apply(self, base: &Base) -> VRef<'_, T> {
let ptr = base as *const Base as *const u8;
unsafe {
VRef::from_raw(
NonNull::from(self.vtable),
NonNull::new_unchecked(ptr.add(self.offset) as *mut _),
)
}
}
#[inline]
pub fn apply_mut(self, base: &mut Base) -> VRefMut<'_, T> {
let ptr = base as *mut Base as *mut u8;
unsafe {
VRefMut::from_raw(
NonNull::from(self.vtable),
NonNull::new_unchecked(ptr.add(self.offset)),
)
}
}
#[inline]
pub fn new<X: HasStaticVTable<T>>(o: FieldOffset<Base, X, Flag>) -> Self {
Self { vtable: X::static_vtable(), offset: o.get_byte_offset(), phantom: PhantomData }
}
#[inline]
pub unsafe fn from_raw(vtable: &'static T::VTable, offset: usize) -> Self {
Self { vtable, offset, phantom: PhantomData }
}
}
impl<Base, T: ?Sized + VTableMeta> VOffset<Base, T, AllowPin> {
#[inline]
pub fn apply_pin(self, base: Pin<&Base>) -> Pin<VRef<'_, T>> {
let ptr = base.get_ref() as *const Base as *mut u8;
unsafe {
Pin::new_unchecked(VRef::from_raw(
NonNull::from(self.vtable),
NonNull::new_unchecked(ptr.add(self.offset)),
))
}
}
}
impl<Base, T: ?Sized + VTableMeta, Flag> Copy for VOffset<Base, T, Flag> {}
impl<Base, T: ?Sized + VTableMeta, Flag> Clone for VOffset<Base, T, Flag> {
fn clone(&self) -> Self {
*self
}
}
#[cfg(doctest)]
mod compile_fail_tests;
#[doc(hidden)]
pub mod internal {
pub use alloc::alloc::dealloc;
pub use alloc::boxed::Box;
}