use std::any::TypeId;
use std::marker::PhantomData;
use fxhash::FxHashMap;
use mopa::Any;
use {Resource, Resources};
struct Invariant<T: ?Sized>(*mut T);
unsafe impl<T> Send for Invariant<T>
where
T: ?Sized,
{
}
unsafe impl<T> Sync for Invariant<T>
where
T: ?Sized,
{
}
pub trait CastFrom<T> {
fn cast(t: &T) -> &Self;
fn cast_mut(t: &mut T) -> &mut Self;
}
pub struct MetaIter<'a, T: ?Sized + 'a> {
fat: &'a [Fat],
index: usize,
res: &'a Resources,
tys: &'a [TypeId],
marker: PhantomData<Invariant<T>>,
}
impl<'a, T> Iterator for MetaIter<'a, T>
where
T: ?Sized + 'a,
{
type Item = &'a T;
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
let index = self.index;
self.index += 1;
unsafe {
self.res
.try_fetch_internal(match self.tys.get(index) {
Some(&x) => x,
None => return None,
})
.map(|res| self.fat[index].create_ptr::<T>(Box::as_ref(&res.borrow())
as *const Resource as *const ())
)
.map(|ptr| &*ptr)
.or_else(|| self.next())
}
}
}
struct Fat(usize);
impl Fat {
pub unsafe fn from_ptr<T: ?Sized>(t: &T) -> Self {
use std::ptr::read;
assert_unsized::<T>();
let fat_ptr = &t as *const &T as *const usize;
let vtable = read::<usize>(fat_ptr.offset(1));
Fat(vtable)
}
pub unsafe fn create_ptr<T: ?Sized>(&self, ptr: *const ()) -> *const T {
let fat_ptr: (*const (), usize) = (ptr, self.0);
*(&fat_ptr as *const (*const (), usize) as *const *const T)
}
}
pub struct MetaIterMut<'a, T: ?Sized + 'a> {
fat: &'a [Fat],
index: usize,
res: &'a Resources,
tys: &'a [TypeId],
marker: PhantomData<Invariant<T>>,
}
impl<'a, T> Iterator for MetaIterMut<'a, T>
where
T: ?Sized + 'a,
{
type Item = &'a mut T;
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
let index = self.index;
self.index += 1;
unsafe {
self.res
.try_fetch_internal(match self.tys.get(index) {
Some(&x) => x,
None => return None,
})
.map(|res| self.fat[index].create_ptr::<T>(
Box::as_mut(&mut res.borrow_mut()) as *mut Resource as *const ()) as *mut T
)
.map(|ptr| &mut *ptr)
.or_else(|| self.next())
}
}
}
pub struct MetaTable<T: ?Sized> {
fat: Vec<Fat>,
indices: FxHashMap<TypeId, usize>,
tys: Vec<TypeId>,
marker: PhantomData<Invariant<T>>,
}
impl<T: ?Sized> MetaTable<T> {
pub fn new() -> Self {
assert_unsized::<T>();
Default::default()
}
pub fn register<R>(&mut self, r: &R)
where
R: Resource,
T: CastFrom<R> + 'static,
{
use std::collections::hash_map::Entry;
let fat = unsafe { Fat::from_ptr(<T as CastFrom<R>>::cast(r)) };
let ty_id = TypeId::of::<R>();
let len = self.indices.len();
match self.indices.entry(ty_id) {
Entry::Occupied(occ) => {
let ind = *occ.get();
self.fat[ind] = fat;
}
Entry::Vacant(vac) => {
vac.insert(len);
self.fat.push(fat);
self.tys.push(ty_id);
}
}
}
pub fn get<'a>(&self, res: &'a Resource) -> Option<&'a T> {
unsafe {
self.indices
.get(&Any::get_type_id(res))
.map(move |&ind| &*self.fat[ind].create_ptr(res as *const _ as *const ()))
}
}
pub fn get_mut<'a>(&self, res: &'a Resource) -> Option<&'a mut T> {
unsafe {
self.indices.get(&Any::get_type_id(res)).map(move |&ind| {
&mut *(self.fat[ind].create_ptr::<T>(res as *const _ as *const ()) as *mut T)
})
}
}
pub fn iter<'a>(&'a self, res: &'a Resources) -> MetaIter<'a, T> {
MetaIter {
fat: &self.fat,
index: 0,
res,
tys: &self.tys,
marker: PhantomData,
}
}
pub fn iter_mut<'a>(&'a self, res: &'a Resources) -> MetaIterMut<'a, T> {
MetaIterMut {
fat: &self.fat,
index: 0,
res,
tys: &self.tys,
marker: PhantomData,
}
}
}
impl<T> Default for MetaTable<T>
where
T: ?Sized,
{
fn default() -> Self {
MetaTable {
fat: Default::default(),
indices: Default::default(),
tys: Default::default(),
marker: Default::default(),
}
}
}
fn assert_unsized<T: ?Sized>() {
use std::mem::size_of;
assert_eq!(size_of::<&T>(), 2 * size_of::<usize>());
}
#[cfg(test)]
mod tests {
use super::*;
use Resources;
trait Object {
fn method1(&self) -> i32;
fn method2(&mut self, x: i32);
}
impl<T> CastFrom<T> for Object
where
T: Object + 'static,
{
fn cast(t: &T) -> &Self {
t
}
fn cast_mut(t: &mut T) -> &mut Self {
t
}
}
struct ImplementorA(i32);
impl Object for ImplementorA {
fn method1(&self) -> i32 {
self.0
}
fn method2(&mut self, x: i32) {
self.0 += x;
}
}
struct ImplementorB(i32);
impl Object for ImplementorB {
fn method1(&self) -> i32 {
self.0
}
fn method2(&mut self, x: i32) {
self.0 *= x;
}
}
#[test]
fn test_iter_all() {
let mut res = Resources::new();
res.insert(ImplementorA(3));
res.insert(ImplementorB(1));
let mut table = MetaTable::<Object>::new();
table.register(&ImplementorA(125));
table.register(&ImplementorB(111111));
{
let mut iter = table.iter(&mut res);
assert_eq!(iter.next().unwrap().method1(), 3);
assert_eq!(iter.next().unwrap().method1(), 1);
}
{
let mut iter_mut = table.iter_mut(&mut res);
let obj = iter_mut.next().unwrap();
obj.method2(3);
assert_eq!(obj.method1(), 6);
let obj = iter_mut.next().unwrap();
obj.method2(4);
assert_eq!(obj.method1(), 4);
}
}
struct ImplementorC;
impl Object for ImplementorC {
fn method1(&self) -> i32 {
33
}
fn method2(&mut self, _x: i32) {
unimplemented!()
}
}
struct ImplementorD;
impl Object for ImplementorD {
fn method1(&self) -> i32 {
42
}
fn method2(&mut self, _x: i32) {
unimplemented!()
}
}
#[test]
fn get() {
let mut res = Resources::new();
res.insert(ImplementorC);
res.insert(ImplementorD);
let mut table = MetaTable::<Object>::new();
table.register(&ImplementorC);
table.register(&ImplementorD);
assert_eq!(
table.get(&*res.fetch::<ImplementorC>()).unwrap().method1(),
33
);
assert_eq!(
table.get(&*res.fetch::<ImplementorD>()).unwrap().method1(),
42
);
res.insert(table);
}
}