2023-11-29 04:25:47 +00:00
|
|
|
use std::{any::TypeId, alloc::Layout, cell::{RefCell, Ref, RefMut}, ptr::NonNull, alloc};
|
|
|
|
|
|
|
|
/// Shorthand for `Send + Sync + 'static`, so it never needs to be implemented manually.
|
2023-12-01 04:05:06 +00:00
|
|
|
pub trait ResourceObject: Send + Sync + 'static {}
|
|
|
|
impl<T: Send + Sync + 'static> ResourceObject for T {}
|
2023-11-29 04:25:47 +00:00
|
|
|
|
|
|
|
/// A type erased storage for a Resource.
|
|
|
|
///
|
|
|
|
/// A pointer instead of a generic is used since Rust may have no idea of the type of the data.
|
|
|
|
pub struct ResourceData {
|
|
|
|
data: RefCell<NonNull<u8>>,
|
|
|
|
type_id: TypeId,
|
|
|
|
layout: Layout,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ResourceData {
|
2023-11-30 04:21:27 +00:00
|
|
|
pub fn new<T: 'static>(data: T) -> Self {
|
2023-11-29 04:25:47 +00:00
|
|
|
let layout = Layout::new::<T>();
|
2023-11-30 04:21:27 +00:00
|
|
|
let data = NonNull::from(&data).cast();
|
|
|
|
|
2023-11-29 04:25:47 +00:00
|
|
|
let ptr = unsafe {
|
|
|
|
if let Some(ptr) = NonNull::new(alloc::alloc(layout)) {
|
2023-11-30 04:21:27 +00:00
|
|
|
std::ptr::copy_nonoverlapping(data.as_ptr(), ptr.as_ptr(), layout.size());
|
2023-11-29 04:25:47 +00:00
|
|
|
ptr
|
|
|
|
} else {
|
|
|
|
alloc::handle_alloc_error(layout)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Self {
|
|
|
|
data: RefCell::new(ptr),
|
|
|
|
type_id: TypeId::of::<T>(),
|
|
|
|
layout,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a boolean indicating whether or not T is of the same type that is inside this Resource
|
|
|
|
pub fn is<T: 'static>(&self) -> bool {
|
|
|
|
self.type_id == TypeId::of::<T>()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Borrow the data inside of the resource.
|
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
/// If the type of T is not the same as the expected type, it will panic.
|
|
|
|
pub fn get<'a, T: 'static>(&'a self) -> Ref<'a, T> {
|
|
|
|
assert!(TypeId::of::<T>() == self.type_id);
|
|
|
|
|
|
|
|
let data = self.data.borrow();
|
|
|
|
Ref::map(data, |ptr| unsafe { &*ptr.cast().as_ptr() })
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Mutably borrow the data inside of the resource.
|
|
|
|
///
|
2023-11-30 04:21:27 +00:00
|
|
|
/// # Panics
|
2023-11-29 04:25:47 +00:00
|
|
|
///
|
2023-11-30 04:21:27 +00:00
|
|
|
/// * If the type of T is not the same as the expected type, it will panic.
|
|
|
|
/// * If the data is already borrowed mutably, this will panic.
|
2023-11-29 04:25:47 +00:00
|
|
|
pub fn get_mut<'a, T: 'static>(&'a self) -> RefMut<'a, T> {
|
|
|
|
assert!(TypeId::of::<T>() == self.type_id);
|
|
|
|
|
|
|
|
let data = self.data.borrow_mut();
|
|
|
|
RefMut::map(data, |ptr| unsafe { &mut *ptr.cast().as_ptr() })
|
|
|
|
}
|
2023-11-30 04:21:27 +00:00
|
|
|
|
|
|
|
/// Borrow the data inside of the resource.
|
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
/// If the type of T is not the same as the expected type, it will panic.
|
|
|
|
pub fn try_get<'a, T: 'static>(&'a self) -> Option<Ref<'a, T>> {
|
|
|
|
assert!(TypeId::of::<T>() == self.type_id);
|
|
|
|
|
|
|
|
self.data.try_borrow()
|
|
|
|
.map(|r| Ref::map(r, |ptr| unsafe { &*ptr.cast().as_ptr() }))
|
|
|
|
.ok()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Mutably borrow the data inside of the resource.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// * If the type of T is not the same as the expected type, it will panic.
|
|
|
|
/// * If the data is already borrowed mutably, this will panic.
|
|
|
|
pub fn try_get_mut<'a, T: 'static>(&'a self) -> Option<RefMut<'a, T>> {
|
|
|
|
assert!(TypeId::of::<T>() == self.type_id);
|
|
|
|
|
|
|
|
self.data.try_borrow_mut()
|
|
|
|
.map(|r| RefMut::map(r, |ptr| unsafe { &mut *ptr.cast().as_ptr() }))
|
|
|
|
.ok()
|
|
|
|
}
|
2023-11-29 04:25:47 +00:00
|
|
|
}
|