1use std::{
2 fmt::{Debug, Formatter, LowerHex, Pointer, UpperHex},
3 hash::{Hash, Hasher},
4 marker::PhantomData,
5 num::NonZeroU16,
6 ptr::NonNull,
7};
8
9use crate::{EntityAlloc, IAllocPolicy, chunk::Unit, gen_index::GenIndex};
10
11pub trait IPoliciedID: Copy + Eq + Debug {
26 type ObjectT: Sized;
28 type PolicyT: IAllocPolicy;
30 type BackID: IEntityAllocID<Self::ObjectT, Self::PolicyT>;
32
33 fn from_backend(ptr: Self::BackID) -> Self;
35 fn into_backend(self) -> Self::BackID;
37
38 fn try_deref_alloc(self, alloc: &IDBoundAlloc<Self>) -> Option<&Self::ObjectT> {
40 self.into_backend().try_deref(alloc)
41 }
42 fn try_deref_alloc_mut(self, alloc: &mut IDBoundAlloc<Self>) -> Option<&mut Self::ObjectT> {
44 self.into_backend().try_deref_mut(alloc)
45 }
46
47 fn deref_alloc(self, alloc: &IDBoundAlloc<Self>) -> &Self::ObjectT {
49 self.into_backend().deref(alloc)
50 }
51 fn deref_alloc_mut(self, alloc: &mut IDBoundAlloc<Self>) -> &mut Self::ObjectT {
53 self.into_backend().deref_mut(alloc)
54 }
55}
56
57pub type IDBoundAlloc<I> = EntityAlloc<<I as IPoliciedID>::ObjectT, <I as IPoliciedID>::PolicyT>;
61
62pub trait IEntityAllocID<E, P: IAllocPolicy>: Sized + Copy {
74 fn from_ptr(alloc: &EntityAlloc<E, P>, ptr: PtrID<E, P>) -> Option<Self>;
76
77 fn from_index(alloc: &EntityAlloc<E, P>, indexed: IndexedID<E, P>) -> Option<Self>;
79
80 fn try_deref(self, alloc: &EntityAlloc<E, P>) -> Option<&E>;
82
83 fn try_deref_mut(self, alloc: &mut EntityAlloc<E, P>) -> Option<&mut E>;
85
86 fn to_index(self, alloc: &EntityAlloc<E, P>) -> Option<IndexedID<E, P>>;
88
89 fn to_ptr(self, alloc: &EntityAlloc<E, P>) -> Option<PtrID<E, P>>;
91
92 #[inline]
94 fn deref(self, alloc: &EntityAlloc<E, P>) -> &E {
95 self.try_deref(alloc).expect("UAF detected!")
96 }
97 #[inline]
99 fn deref_mut(self, alloc: &mut EntityAlloc<E, P>) -> &mut E {
100 self.try_deref_mut(alloc).expect("UAF detected!")
101 }
102
103 fn allocate_from(alloc: &EntityAlloc<E, P>, val: E) -> Self;
107
108 fn free(self, alloc: &mut EntityAlloc<E, P>) -> Option<E>;
112}
113
114pub struct PtrID<E, P> {
126 pub(crate) ptr: NonNull<Unit<E>>,
127 _marker: PhantomData<P>,
128}
129impl<E, P> From<NonNull<Unit<E>>> for PtrID<E, P> {
130 fn from(ptr: NonNull<Unit<E>>) -> Self {
131 Self {
132 ptr,
133 _marker: PhantomData,
134 }
135 }
136}
137impl<E, P> From<&mut Unit<E>> for PtrID<E, P> {
138 fn from(unit: &mut Unit<E>) -> Self {
139 Self {
140 ptr: NonNull::from(unit),
141 _marker: PhantomData,
142 }
143 }
144}
145impl<E, P> From<*mut Unit<E>> for PtrID<E, P> {
146 fn from(raw: *mut Unit<E>) -> Self {
147 Self {
148 ptr: NonNull::new(raw).expect("PtrID cannot be null"),
149 _marker: PhantomData,
150 }
151 }
152}
153impl<E, P> Copy for PtrID<E, P> {}
154impl<E, P> Clone for PtrID<E, P> {
155 fn clone(&self) -> Self {
156 *self
157 }
158}
159impl<E, P> Debug for PtrID<E, P> {
160 #[inline]
161 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
162 Debug::fmt(&self.ptr, f)
163 }
164}
165impl<E, P> Pointer for PtrID<E, P> {
166 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
167 Pointer::fmt(&self.ptr, f)
168 }
169}
170
171impl<E, P> PartialEq for PtrID<E, P> {
172 #[inline]
173 fn eq(&self, other: &Self) -> bool {
174 self.ptr == other.ptr
175 }
176}
177impl<E, P> Eq for PtrID<E, P> {}
178impl<E, P> PartialOrd for PtrID<E, P> {
179 #[inline]
180 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
181 Some(self.cmp(other))
182 }
183}
184impl<E, P> Ord for PtrID<E, P> {
185 #[inline]
186 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
187 self.ptr.cmp(&other.ptr)
188 }
189}
190impl<E, P> Hash for PtrID<E, P> {
191 #[inline]
192 fn hash<H: Hasher>(&self, state: &mut H) {
193 self.ptr.hash(state);
194 }
195}
196unsafe impl<E: Send, P> Send for PtrID<E, P> {}
197unsafe impl<E: Sync, P> Sync for PtrID<E, P> {}
198impl<E, P: IAllocPolicy> IEntityAllocID<E, P> for PtrID<E, P> {
199 fn from_ptr(_: &EntityAlloc<E, P>, ptr: PtrID<E, P>) -> Option<Self> {
200 Some(ptr)
201 }
202 fn from_index(alloc: &EntityAlloc<E, P>, indexed: IndexedID<E, P>) -> Option<Self> {
203 let unit_ptr = alloc.unit_ptr_of_indexed(indexed.indexed).ok()?;
204 Some(Self::from(unit_ptr))
205 }
206 fn try_deref(self, alloc: &EntityAlloc<E, P>) -> Option<&E> {
207 if cfg!(debug_assertions) {
208 alloc.check_unit_validity(self.ptr.as_ptr()).ok()?;
210 }
211 unsafe { self.ptr.as_ref().as_init_ref() }
212 }
213 fn try_deref_mut(mut self, alloc: &mut EntityAlloc<E, P>) -> Option<&mut E> {
214 if cfg!(debug_assertions) {
215 alloc.check_unit_validity(self.ptr.as_ptr()).ok()?;
217 }
218 unsafe { self.ptr.as_mut().as_init_mut() }
219 }
220 fn to_index(self, alloc: &EntityAlloc<E, P>) -> Option<IndexedID<E, P>> {
221 let gen_index = alloc.index_of_unit_ptr(self.ptr.as_ptr()).ok()?;
222 Some(IndexedID::from(gen_index))
223 }
224 fn to_ptr(self, _alloc: &EntityAlloc<E, P>) -> Option<PtrID<E, P>> {
225 Some(self)
226 }
227
228 fn allocate_from(alloc: &EntityAlloc<E, P>, val: E) -> Self {
229 let ptr = match alloc.try_allocate_unit(val) {
230 Ok(ptr) => NonNull::new(ptr as *mut _).unwrap(),
231 Err(..) => panic!("Allocation failed in IEntityAllocID::allocate_from"),
232 };
233 PtrID {
234 ptr,
235 _marker: PhantomData,
236 }
237 }
238 fn free(self, alloc: &mut EntityAlloc<E, P>) -> Option<E> {
239 alloc.free_unit_ptr(self.ptr.as_ptr())
240 }
241}
242impl<E, P: IAllocPolicy> IPoliciedID for PtrID<E, P> {
243 type ObjectT = E;
244 type PolicyT = P;
245 type BackID = PtrID<E, P>;
246
247 fn from_backend(ptr: Self::BackID) -> Self {
248 ptr
249 }
250 fn into_backend(self) -> Self::BackID {
251 self
252 }
253}
254impl<E, P: IAllocPolicy> PtrID<E, P> {
255 pub fn check_validity(&self, alloc: &EntityAlloc<E, P>) -> bool {
263 alloc.index_of_unit_ptr(self.ptr.as_ptr()).is_ok()
264 }
265
266 pub unsafe fn direct_deref<'a>(self) -> Option<&'a E> {
272 unsafe { self.ptr.as_ref().as_init_ref() }
273 }
274
275 pub unsafe fn direct_deref_mut<'a>(mut self) -> Option<&'a mut E> {
281 unsafe { self.ptr.as_mut().as_init_mut() }
282 }
283}
284
285#[repr(C)]
293pub struct IndexedID<E, P> {
294 pub indexed: GenIndex,
296 _marker: PhantomData<(E, P)>,
297}
298impl<E, P> From<GenIndex> for IndexedID<E, P> {
299 fn from(indexed: GenIndex) -> Self {
300 Self {
301 indexed,
302 _marker: PhantomData,
303 }
304 }
305}
306impl<E, P> Copy for IndexedID<E, P> {}
307impl<E, P> Clone for IndexedID<E, P> {
308 fn clone(&self) -> Self {
309 *self
310 }
311}
312impl<E, P> Debug for IndexedID<E, P> {
313 #[inline]
314 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
315 let (real, gene) = self.indexed.tear();
316 write!(f, "IndexedID({real:x} gen {gene})")
317 }
318}
319impl<E, P> Pointer for IndexedID<E, P> {
320 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
321 write!(f, "{:#x}", u64::from(self.indexed))
322 }
323}
324impl<E, P> LowerHex for IndexedID<E, P> {
325 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
326 LowerHex::fmt(&u64::from(self.indexed), f)
327 }
328}
329impl<E, P> UpperHex for IndexedID<E, P> {
330 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
331 UpperHex::fmt(&u64::from(self.indexed), f)
332 }
333}
334impl<E, P> PartialEq for IndexedID<E, P> {
335 #[inline]
336 fn eq(&self, other: &Self) -> bool {
337 self.indexed == other.indexed
338 }
339}
340impl<E, P> Eq for IndexedID<E, P> {}
341impl<E, P> PartialOrd for IndexedID<E, P> {
342 #[inline]
343 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
344 Some(self.cmp(other))
345 }
346}
347impl<E, P> Ord for IndexedID<E, P> {
348 #[inline]
349 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
350 self.indexed.cmp(&other.indexed)
351 }
352}
353impl<E, P> Hash for IndexedID<E, P> {
354 #[inline]
355 fn hash<H: Hasher>(&self, state: &mut H) {
356 self.indexed.hash(state);
357 }
358}
359#[cfg(feature = "serde")]
360impl<E, P> serde_core::Serialize for IndexedID<E, P> {
362 fn serialize<S: serde_core::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
363 use std::fmt::Write;
364 #[derive(Default)]
365 struct Buffer {
366 buf: [u8; 32],
367 len: usize,
368 }
369 impl Buffer {
370 fn as_str(&self) -> &str {
371 unsafe { std::str::from_utf8_unchecked(&self.buf[..self.len]) }
373 }
374 }
375 impl Write for Buffer {
376 fn write_str(&mut self, s: &str) -> std::fmt::Result {
377 let bytes = s.as_bytes();
378 let avail = self.buf.len().saturating_sub(self.len);
379 if bytes.len() > avail {
380 return Err(std::fmt::Error);
381 }
382 let dst = &mut self.buf[self.len..self.len + bytes.len()];
383 dst.copy_from_slice(bytes);
384 self.len += bytes.len();
385 Ok(())
386 }
387 }
388
389 let (real, gene) = self.indexed.tear();
390 let mut buf = Buffer::default();
391 write!(&mut buf, "{real:x}:{gene:x}").unwrap();
392 serializer.serialize_str(buf.as_str())
393 }
394}
395#[cfg(feature = "serde")]
396impl<'de, E, P> serde_core::Deserialize<'de> for IndexedID<E, P> {
397 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
398 where
399 D: serde_core::Deserializer<'de>,
400 {
401 use serde_core::de::Error as _;
402
403 let s = <&str>::deserialize(deserializer)?;
404 let mut parts = s.splitn(2, ':');
405 let real_str = parts
406 .next()
407 .ok_or_else(|| D::Error::custom("missing real index"))?;
408 let gen_str = parts
409 .next()
410 .ok_or_else(|| D::Error::custom("missing generation"))?;
411
412 let real_u64 = u64::from_str_radix(real_str, 16)
413 .map_err(|e| D::Error::custom(format!("invalid real index: {e}")))?;
414 let gen_u64 = u64::from_str_radix(gen_str, 16)
415 .map_err(|e| D::Error::custom(format!("invalid generation: {e}")))?;
416
417 if gen_u64 > u16::MAX as u64 || gen_u64 == 0 {
418 return Err(D::Error::custom("generation out of range"));
419 }
420
421 let real_usize = real_u64 as usize;
422 let generation = NonZeroU16::new(gen_u64 as u16).unwrap();
424
425 Ok(IndexedID::from(GenIndex::compose(real_usize, generation)))
426 }
427}
428
429impl<E, P: IAllocPolicy> IEntityAllocID<E, P> for IndexedID<E, P> {
430 fn from_ptr(alloc: &EntityAlloc<E, P>, ptr: PtrID<E, P>) -> Option<Self> {
431 let gen_index = alloc.index_of_unit_ptr(ptr.ptr.as_ptr()).ok()?;
432 Some(IndexedID::from(gen_index))
433 }
434 fn from_index(_: &EntityAlloc<E, P>, indexed: IndexedID<E, P>) -> Option<Self> {
435 Some(indexed)
436 }
437
438 fn try_deref(self, alloc: &EntityAlloc<E, P>) -> Option<&E> {
439 let unit_ptr = alloc.unit_ptr_of_indexed(self.indexed).ok()?;
440 unsafe { unit_ptr.as_ref().as_init_ref() }
441 }
442 fn try_deref_mut(self, alloc: &mut EntityAlloc<E, P>) -> Option<&mut E> {
443 alloc
444 .unit_mut_of_indexed(self.indexed)
445 .ok()
446 .and_then(|p| p.as_init_mut())
447 }
448 fn to_index(self, _: &EntityAlloc<E, P>) -> Option<IndexedID<E, P>> {
449 Some(self)
450 }
451 fn to_ptr(self, alloc: &EntityAlloc<E, P>) -> Option<PtrID<E, P>> {
452 let unit_ptr = alloc.unit_ptr_of_indexed(self.indexed).ok()?;
453 Some(PtrID::from(unit_ptr))
454 }
455
456 fn allocate_from(alloc: &EntityAlloc<E, P>, val: E) -> Self {
457 let Ok(unit) = alloc.try_allocate_unit(val) else {
458 panic!("Allocation failed in IEntityAllocID::allocate_from");
459 };
460 unsafe { Self::from(unit.as_ref().unwrap().indexed) }
461 }
462 fn free(self, alloc: &mut EntityAlloc<E, P>) -> Option<E> {
463 alloc.free_gen_index(self.indexed)
464 }
465}
466impl<E, P: IAllocPolicy> IPoliciedID for IndexedID<E, P> {
467 type ObjectT = E;
468 type PolicyT = P;
469 type BackID = IndexedID<E, P>;
470
471 fn from_backend(ptr: Self::BackID) -> Self {
472 ptr
473 }
474 fn into_backend(self) -> Self::BackID {
475 self
476 }
477}
478impl<E, P: IAllocPolicy> IndexedID<E, P> {
479 pub fn next_to_alloc(alloc: &EntityAlloc<E, P>) -> Self {
483 Self::from(alloc.next_index())
484 }
485
486 pub fn get_generation(self) -> NonZeroU16 {
490 self.indexed.generation()
491 }
492
493 pub fn get_order(self) -> usize {
497 self.indexed.real_index()
498 }
499}