use std::any::TypeId;
use std::collections::HashMap;
use std::ffi::c_void;
use std::ptr::NonNull;
use boxdd_sys::ffi;
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub(crate) struct IdKey {
pub(crate) index1: i32,
pub(crate) world0: u16,
pub(crate) generation: u16,
}
impl From<ffi::b2BodyId> for IdKey {
#[inline]
fn from(id: ffi::b2BodyId) -> Self {
Self {
index1: id.index1,
world0: id.world0,
generation: id.generation,
}
}
}
impl From<ffi::b2ShapeId> for IdKey {
#[inline]
fn from(id: ffi::b2ShapeId) -> Self {
Self {
index1: id.index1,
world0: id.world0,
generation: id.generation,
}
}
}
impl From<ffi::b2JointId> for IdKey {
#[inline]
fn from(id: ffi::b2JointId) -> Self {
Self {
index1: id.index1,
world0: id.world0,
generation: id.generation,
}
}
}
pub(crate) struct ErasedUserData {
type_id: TypeId,
ptr: NonNull<u8>,
drop_fn: unsafe fn(*mut c_void),
}
impl ErasedUserData {
#[inline]
pub(crate) fn new<T: 'static>(value: T) -> Self {
unsafe fn drop_boxed<T: 'static>(p: *mut c_void) {
drop(unsafe { Box::from_raw(p as *mut T) });
}
let boxed = Box::new(value);
let ptr =
NonNull::new(Box::into_raw(boxed) as *mut u8).expect("Box::into_raw returned null");
Self {
type_id: TypeId::of::<T>(),
ptr,
drop_fn: drop_boxed::<T>,
}
}
#[inline]
pub(crate) fn as_ptr(&self) -> *mut c_void {
self.ptr.as_ptr() as *mut c_void
}
#[inline]
pub(crate) fn with_ref<T: 'static, R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
if self.type_id != TypeId::of::<T>() {
return None;
}
let r = unsafe { &*(self.ptr.as_ptr() as *const T) };
Some(f(r))
}
#[inline]
pub(crate) fn matches<T: 'static>(&self) -> bool {
self.type_id == TypeId::of::<T>()
}
#[inline]
pub(crate) fn with_mut<T: 'static, R>(&mut self, f: impl FnOnce(&mut T) -> R) -> Option<R> {
if self.type_id != TypeId::of::<T>() {
return None;
}
let r = unsafe { &mut *(self.ptr.as_ptr() as *mut T) };
Some(f(r))
}
pub(crate) fn try_into_value<T: 'static>(self) -> Result<T, Self> {
if self.type_id != TypeId::of::<T>() {
return Err(self);
}
let p = self.ptr;
core::mem::forget(self);
Ok(*unsafe { Box::from_raw(p.as_ptr() as *mut T) })
}
}
impl Drop for ErasedUserData {
fn drop(&mut self) {
unsafe { (self.drop_fn)(self.ptr.as_ptr() as *mut c_void) }
}
}
#[derive(Default)]
pub(crate) struct UserDataStore {
pub(crate) world: Option<ErasedUserData>,
pub(crate) bodies: HashMap<IdKey, ErasedUserData>,
pub(crate) shapes: HashMap<IdKey, ErasedUserData>,
pub(crate) joints: HashMap<IdKey, ErasedUserData>,
}