use std::{
fmt::{Debug, Formatter, LowerHex, Pointer, UpperHex},
hash::{Hash, Hasher},
marker::PhantomData,
num::NonZeroU16,
ptr::NonNull,
};
use crate::{EntityAlloc, IAllocPolicy, chunk::Unit, gen_index::GenIndex};
pub trait IPoliciedID: Copy + Eq + Debug {
type ObjectT: Sized;
type PolicyT: IAllocPolicy;
type BackID: IEntityAllocID<Self::ObjectT, Self::PolicyT>;
fn from_backend(ptr: Self::BackID) -> Self;
fn into_backend(self) -> Self::BackID;
fn try_deref_alloc(self, alloc: &IDBoundAlloc<Self>) -> Option<&Self::ObjectT> {
self.into_backend().try_deref(alloc)
}
fn try_deref_alloc_mut(self, alloc: &mut IDBoundAlloc<Self>) -> Option<&mut Self::ObjectT> {
self.into_backend().try_deref_mut(alloc)
}
fn deref_alloc(self, alloc: &IDBoundAlloc<Self>) -> &Self::ObjectT {
self.into_backend().deref(alloc)
}
fn deref_alloc_mut(self, alloc: &mut IDBoundAlloc<Self>) -> &mut Self::ObjectT {
self.into_backend().deref_mut(alloc)
}
}
pub type IDBoundAlloc<I> = EntityAlloc<<I as IPoliciedID>::ObjectT, <I as IPoliciedID>::PolicyT>;
pub trait IEntityAllocID<E, P: IAllocPolicy>: Sized + Copy {
fn from_ptr(alloc: &EntityAlloc<E, P>, ptr: PtrID<E, P>) -> Option<Self>;
fn from_index(alloc: &EntityAlloc<E, P>, indexed: IndexedID<E, P>) -> Option<Self>;
fn try_deref(self, alloc: &EntityAlloc<E, P>) -> Option<&E>;
fn try_deref_mut(self, alloc: &mut EntityAlloc<E, P>) -> Option<&mut E>;
fn to_index(self, alloc: &EntityAlloc<E, P>) -> Option<IndexedID<E, P>>;
fn to_ptr(self, alloc: &EntityAlloc<E, P>) -> Option<PtrID<E, P>>;
#[inline]
fn deref(self, alloc: &EntityAlloc<E, P>) -> &E {
self.try_deref(alloc).expect("UAF detected!")
}
#[inline]
fn deref_mut(self, alloc: &mut EntityAlloc<E, P>) -> &mut E {
self.try_deref_mut(alloc).expect("UAF detected!")
}
fn allocate_from(alloc: &EntityAlloc<E, P>, val: E) -> Self;
fn free(self, alloc: &mut EntityAlloc<E, P>) -> Option<E>;
}
pub struct PtrID<E, P> {
pub(crate) ptr: NonNull<Unit<E>>,
_marker: PhantomData<P>,
}
impl<E, P> From<NonNull<Unit<E>>> for PtrID<E, P> {
fn from(ptr: NonNull<Unit<E>>) -> Self {
Self {
ptr,
_marker: PhantomData,
}
}
}
impl<E, P> From<&mut Unit<E>> for PtrID<E, P> {
fn from(unit: &mut Unit<E>) -> Self {
Self {
ptr: NonNull::from(unit),
_marker: PhantomData,
}
}
}
impl<E, P> From<*mut Unit<E>> for PtrID<E, P> {
fn from(raw: *mut Unit<E>) -> Self {
Self {
ptr: NonNull::new(raw).expect("PtrID cannot be null"),
_marker: PhantomData,
}
}
}
impl<E, P> Copy for PtrID<E, P> {}
impl<E, P> Clone for PtrID<E, P> {
fn clone(&self) -> Self {
*self
}
}
impl<E, P> Debug for PtrID<E, P> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Debug::fmt(&self.ptr, f)
}
}
impl<E, P> Pointer for PtrID<E, P> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Pointer::fmt(&self.ptr, f)
}
}
impl<E, P> PartialEq for PtrID<E, P> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.ptr == other.ptr
}
}
impl<E, P> Eq for PtrID<E, P> {}
impl<E, P> PartialOrd for PtrID<E, P> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<E, P> Ord for PtrID<E, P> {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.ptr.cmp(&other.ptr)
}
}
impl<E, P> Hash for PtrID<E, P> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.ptr.hash(state);
}
}
unsafe impl<E: Send, P> Send for PtrID<E, P> {}
unsafe impl<E: Sync, P> Sync for PtrID<E, P> {}
impl<E, P: IAllocPolicy> IEntityAllocID<E, P> for PtrID<E, P> {
fn from_ptr(_: &EntityAlloc<E, P>, ptr: PtrID<E, P>) -> Option<Self> {
Some(ptr)
}
fn from_index(alloc: &EntityAlloc<E, P>, indexed: IndexedID<E, P>) -> Option<Self> {
let unit_ptr = alloc.unit_ptr_of_indexed(indexed.indexed).ok()?;
Some(Self::from(unit_ptr))
}
fn try_deref(self, alloc: &EntityAlloc<E, P>) -> Option<&E> {
if cfg!(debug_assertions) {
alloc.check_unit_validity(self.ptr.as_ptr()).ok()?;
}
unsafe { self.ptr.as_ref().as_init_ref() }
}
fn try_deref_mut(mut self, alloc: &mut EntityAlloc<E, P>) -> Option<&mut E> {
if cfg!(debug_assertions) {
alloc.check_unit_validity(self.ptr.as_ptr()).ok()?;
}
unsafe { self.ptr.as_mut().as_init_mut() }
}
fn to_index(self, alloc: &EntityAlloc<E, P>) -> Option<IndexedID<E, P>> {
let gen_index = alloc.index_of_unit_ptr(self.ptr.as_ptr()).ok()?;
Some(IndexedID::from(gen_index))
}
fn to_ptr(self, _alloc: &EntityAlloc<E, P>) -> Option<PtrID<E, P>> {
Some(self)
}
fn allocate_from(alloc: &EntityAlloc<E, P>, val: E) -> Self {
let ptr = match alloc.try_allocate_unit(val) {
Ok(ptr) => NonNull::new(ptr as *mut _).unwrap(),
Err(..) => panic!("Allocation failed in IEntityAllocID::allocate_from"),
};
PtrID {
ptr,
_marker: PhantomData,
}
}
fn free(self, alloc: &mut EntityAlloc<E, P>) -> Option<E> {
alloc.free_unit_ptr(self.ptr.as_ptr())
}
}
impl<E, P: IAllocPolicy> IPoliciedID for PtrID<E, P> {
type ObjectT = E;
type PolicyT = P;
type BackID = PtrID<E, P>;
fn from_backend(ptr: Self::BackID) -> Self {
ptr
}
fn into_backend(self) -> Self::BackID {
self
}
}
impl<E, P: IAllocPolicy> PtrID<E, P> {
pub fn check_validity(&self, alloc: &EntityAlloc<E, P>) -> bool {
alloc.index_of_unit_ptr(self.ptr.as_ptr()).is_ok()
}
pub unsafe fn direct_deref<'a>(self) -> Option<&'a E> {
unsafe { self.ptr.as_ref().as_init_ref() }
}
pub unsafe fn direct_deref_mut<'a>(mut self) -> Option<&'a mut E> {
unsafe { self.ptr.as_mut().as_init_mut() }
}
}
#[repr(C)]
pub struct IndexedID<E, P> {
pub indexed: GenIndex,
_marker: PhantomData<(E, P)>,
}
impl<E, P> From<GenIndex> for IndexedID<E, P> {
fn from(indexed: GenIndex) -> Self {
Self {
indexed,
_marker: PhantomData,
}
}
}
impl<E, P> Copy for IndexedID<E, P> {}
impl<E, P> Clone for IndexedID<E, P> {
fn clone(&self) -> Self {
*self
}
}
impl<E, P> Debug for IndexedID<E, P> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let (real, gene) = self.indexed.tear();
write!(f, "IndexedID({real:x} gen {gene})")
}
}
impl<E, P> Pointer for IndexedID<E, P> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:#x}", u64::from(self.indexed))
}
}
impl<E, P> LowerHex for IndexedID<E, P> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
LowerHex::fmt(&u64::from(self.indexed), f)
}
}
impl<E, P> UpperHex for IndexedID<E, P> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
UpperHex::fmt(&u64::from(self.indexed), f)
}
}
impl<E, P> PartialEq for IndexedID<E, P> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.indexed == other.indexed
}
}
impl<E, P> Eq for IndexedID<E, P> {}
impl<E, P> PartialOrd for IndexedID<E, P> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<E, P> Ord for IndexedID<E, P> {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.indexed.cmp(&other.indexed)
}
}
impl<E, P> Hash for IndexedID<E, P> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.indexed.hash(state);
}
}
#[cfg(feature = "serde")]
impl<E, P> serde_core::Serialize for IndexedID<E, P> {
fn serialize<S: serde_core::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use std::fmt::Write;
#[derive(Default)]
struct Buffer {
buf: [u8; 32],
len: usize,
}
impl Buffer {
fn as_str(&self) -> &str {
unsafe { std::str::from_utf8_unchecked(&self.buf[..self.len]) }
}
}
impl Write for Buffer {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
let bytes = s.as_bytes();
let avail = self.buf.len().saturating_sub(self.len);
if bytes.len() > avail {
return Err(std::fmt::Error);
}
let dst = &mut self.buf[self.len..self.len + bytes.len()];
dst.copy_from_slice(bytes);
self.len += bytes.len();
Ok(())
}
}
let (real, gene) = self.indexed.tear();
let mut buf = Buffer::default();
write!(&mut buf, "{real:x}:{gene:x}").unwrap();
serializer.serialize_str(buf.as_str())
}
}
#[cfg(feature = "serde")]
impl<'de, E, P> serde_core::Deserialize<'de> for IndexedID<E, P> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde_core::Deserializer<'de>,
{
use serde_core::de::Error as _;
let s = <&str>::deserialize(deserializer)?;
let mut parts = s.splitn(2, ':');
let real_str = parts
.next()
.ok_or_else(|| D::Error::custom("missing real index"))?;
let gen_str = parts
.next()
.ok_or_else(|| D::Error::custom("missing generation"))?;
let real_u64 = u64::from_str_radix(real_str, 16)
.map_err(|e| D::Error::custom(format!("invalid real index: {e}")))?;
let gen_u64 = u64::from_str_radix(gen_str, 16)
.map_err(|e| D::Error::custom(format!("invalid generation: {e}")))?;
if gen_u64 > u16::MAX as u64 || gen_u64 == 0 {
return Err(D::Error::custom("generation out of range"));
}
let real_usize = real_u64 as usize;
let generation = NonZeroU16::new(gen_u64 as u16).unwrap();
Ok(IndexedID::from(GenIndex::compose(real_usize, generation)))
}
}
impl<E, P: IAllocPolicy> IEntityAllocID<E, P> for IndexedID<E, P> {
fn from_ptr(alloc: &EntityAlloc<E, P>, ptr: PtrID<E, P>) -> Option<Self> {
let gen_index = alloc.index_of_unit_ptr(ptr.ptr.as_ptr()).ok()?;
Some(IndexedID::from(gen_index))
}
fn from_index(_: &EntityAlloc<E, P>, indexed: IndexedID<E, P>) -> Option<Self> {
Some(indexed)
}
fn try_deref(self, alloc: &EntityAlloc<E, P>) -> Option<&E> {
let unit_ptr = alloc.unit_ptr_of_indexed(self.indexed).ok()?;
unsafe { unit_ptr.as_ref().as_init_ref() }
}
fn try_deref_mut(self, alloc: &mut EntityAlloc<E, P>) -> Option<&mut E> {
alloc
.unit_mut_of_indexed(self.indexed)
.ok()
.and_then(|p| p.as_init_mut())
}
fn to_index(self, _: &EntityAlloc<E, P>) -> Option<IndexedID<E, P>> {
Some(self)
}
fn to_ptr(self, alloc: &EntityAlloc<E, P>) -> Option<PtrID<E, P>> {
let unit_ptr = alloc.unit_ptr_of_indexed(self.indexed).ok()?;
Some(PtrID::from(unit_ptr))
}
fn allocate_from(alloc: &EntityAlloc<E, P>, val: E) -> Self {
let Ok(unit) = alloc.try_allocate_unit(val) else {
panic!("Allocation failed in IEntityAllocID::allocate_from");
};
unsafe { Self::from(unit.as_ref().unwrap().indexed) }
}
fn free(self, alloc: &mut EntityAlloc<E, P>) -> Option<E> {
alloc.free_gen_index(self.indexed)
}
}
impl<E, P: IAllocPolicy> IPoliciedID for IndexedID<E, P> {
type ObjectT = E;
type PolicyT = P;
type BackID = IndexedID<E, P>;
fn from_backend(ptr: Self::BackID) -> Self {
ptr
}
fn into_backend(self) -> Self::BackID {
self
}
}
impl<E, P: IAllocPolicy> IndexedID<E, P> {
pub fn next_to_alloc(alloc: &EntityAlloc<E, P>) -> Self {
Self::from(alloc.next_index())
}
pub fn get_generation(self) -> NonZeroU16 {
self.indexed.generation()
}
pub fn get_order(self) -> usize {
self.indexed.real_index()
}
}