use bumpalo::Bump;
use std::{
alloc::Layout, any::TypeId, cell::Cell, marker::PhantomData, num::NonZeroUsize, ptr::NonNull,
};
use sptr::Strict;
use crate::{
bump::BumpRef,
util::{addr_to_ptr, drop_by_addr},
};
pub struct UnsafeObject {
addr: Option<NonZeroUsize>,
type_id: TypeId,
drop_fn: unsafe fn(NonZeroUsize),
_p: PhantomData<Cell<()>>,
}
impl UnsafeObject {
pub unsafe fn new<T>(bump: &Bump, inner: T) -> Self
where
T: Send + 'static,
{
let layout = Layout::new::<T>();
let ptr = bump.alloc_layout(layout);
let ptr = unsafe {
let ptr = ptr.cast::<T>();
ptr.as_ptr().write(inner);
ptr
};
let addr = ptr.as_ptr().addr();
Self {
addr: Some(NonZeroUsize::new(addr).expect("addr shoud not be zero")),
type_id: TypeId::of::<T>(),
drop_fn: drop_by_addr::<T>,
_p: PhantomData,
}
}
#[inline]
pub fn is<T>(&self) -> bool
where
T: 'static,
{
let tid = TypeId::of::<T>();
tid == self.type_id
}
#[inline]
pub unsafe fn downcast_ref<T>(&self) -> Option<&T>
where
T: 'static,
{
if self.is::<T>() {
let ptr: NonNull<T> = addr_to_ptr::<T>(*self.addr.as_ref().unwrap());
let inner = &*ptr.as_ptr();
Some(inner)
} else {
None
}
}
#[inline]
pub unsafe fn downcast_mut<T>(&mut self) -> Option<&mut T>
where
T: 'static,
{
if self.is::<T>() {
let ptr: NonNull<T> = addr_to_ptr::<T>(*self.addr.as_ref().unwrap());
let inner = &mut *ptr.as_ptr();
Some(inner)
} else {
None
}
}
}
impl Drop for UnsafeObject {
fn drop(&mut self) {
if let Some(addr) = self.addr.take() {
unsafe { (self.drop_fn)(addr) };
}
}
}
pub struct BumpObject {
inner: UnsafeObject,
_bump_ref: BumpRef,
}
impl BumpObject {
pub fn new(inner: UnsafeObject, bump_ref: BumpRef) -> Self {
Self {
inner,
_bump_ref: bump_ref,
}
}
}
pub trait BumpAny {
fn is<T>(&self) -> bool
where
T: 'static;
fn downcast_ref<T>(&self) -> Option<&T>
where
T: 'static;
fn downcast_mut<T>(&mut self) -> Option<&mut T>
where
T: 'static;
}
impl BumpAny for BumpObject {
fn is<T>(&self) -> bool
where
T: 'static,
{
self.inner.is::<T>()
}
fn downcast_ref<T>(&self) -> Option<&T>
where
T: 'static,
{
unsafe { self.inner.downcast_ref::<T>() }
}
fn downcast_mut<T>(&mut self) -> Option<&mut T>
where
T: 'static,
{
unsafe { self.inner.downcast_mut::<T>() }
}
}
#[cfg(test)]
mod test {
use crate::util::check_send;
use super::UnsafeObject;
#[test]
fn test_inner_bounds() {
check_send::<UnsafeObject>();
}
}