use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use typenum::Unsigned;
use crate::state::State;
pub unsafe trait IdTrait: Sized + Copy + Clone + PartialEq + Eq + Hash + fmt::Debug {
type IndexBits: Unsigned;
type GenerationBits: Unsigned;
fn new(index: usize, generation: u32) -> Self;
fn index(&self) -> usize;
fn generation(&self) -> u32;
fn matching_state(&self) -> State<Self::GenerationBits>;
fn null() -> Self;
fn is_null(&self) -> bool;
fn max_len() -> usize {
crate::static_assert_index_bits::<Self::IndexBits>();
(u32::MAX >> (32 - Self::IndexBits::U32)) as usize
}
fn max_generation() -> u32 {
crate::static_assert_generation_bits::<Self::GenerationBits>();
(u32::MAX >> 1) >> (31 - Self::GenerationBits::U32)
}
fn reserved_state(&self) -> State<Self::GenerationBits> {
State::new(self.generation() << 1)
}
fn debug_format(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
"Id {{ index: {}, generation {} }}",
self.index(),
self.generation(),
)
}
}
#[repr(transparent)]
pub struct Id32<T, const GENERATION_BITS: usize>(
u32,
PhantomData<fn() -> T>,
);
unsafe impl<T, const GENERATION_BITS: usize> IdTrait for Id32<T, GENERATION_BITS>
where
typenum::Const<GENERATION_BITS>: typenum::ToUInt,
typenum::U<GENERATION_BITS>: Unsigned,
typenum::U32: std::ops::Sub<typenum::U<GENERATION_BITS>>,
<typenum::U32 as std::ops::Sub<typenum::U<GENERATION_BITS>>>::Output: Unsigned,
{
type IndexBits = typenum::Diff<typenum::U32, typenum::U<GENERATION_BITS>>;
type GenerationBits = typenum::U<GENERATION_BITS>;
fn new(index: usize, generation: u32) -> Self {
Self((index << GENERATION_BITS) as u32 | generation, PhantomData)
}
fn index(&self) -> usize {
(self.0 >> GENERATION_BITS) as usize
}
fn generation(&self) -> u32 {
self.0 & !(u32::MAX << GENERATION_BITS)
}
fn matching_state(&self) -> State<Self::GenerationBits> {
State::new((self.generation() << 1) + 1)
}
fn null() -> Self {
Self(u32::MAX, PhantomData)
}
fn is_null(&self) -> bool {
self.0 == u32::MAX
}
}
impl<T, const GENERATION_BITS: usize> Copy for Id32<T, GENERATION_BITS> {}
impl<T, const GENERATION_BITS: usize> Clone for Id32<T, GENERATION_BITS> {
fn clone(&self) -> Self {
Self(self.0, PhantomData)
}
}
impl<T, const GENERATION_BITS: usize> fmt::Debug for Id32<T, GENERATION_BITS>
where
Self: IdTrait,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"Id {{ index: {}, generation {} }}",
self.index(),
self.generation(),
)
}
}
impl<T, const GENERATION_BITS: usize> Hash for Id32<T, GENERATION_BITS> {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
self.0.hash(state);
}
}
impl<T, const GENERATION_BITS: usize> PartialEq for Id32<T, GENERATION_BITS> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T, const GENERATION_BITS: usize> Eq for Id32<T, GENERATION_BITS> {}
#[cfg(test)]
pub use id8::Id8;
#[cfg(test)]
mod id8 {
use super::*;
#[repr(transparent)]
pub struct Id8<T, const GENERATION_BITS: usize>(
u8,
PhantomData<fn() -> T>,
);
unsafe impl<T, const GENERATION_BITS: usize> IdTrait for Id8<T, GENERATION_BITS>
where
typenum::Const<GENERATION_BITS>: typenum::ToUInt,
typenum::U<GENERATION_BITS>: Unsigned,
typenum::U8: std::ops::Sub<typenum::U<GENERATION_BITS>>,
<typenum::U8 as std::ops::Sub<typenum::U<GENERATION_BITS>>>::Output: Unsigned,
{
type IndexBits = typenum::Diff<typenum::U8, typenum::U<GENERATION_BITS>>;
type GenerationBits = typenum::U<GENERATION_BITS>;
fn new(index: usize, generation: u32) -> Self {
Self(
((index << GENERATION_BITS) as u32 | generation) as u8,
PhantomData,
)
}
fn index(&self) -> usize {
(self.0 >> GENERATION_BITS) as usize
}
fn generation(&self) -> u32 {
(self.0 & !(u8::MAX << GENERATION_BITS)) as u32
}
fn matching_state(&self) -> State<Self::GenerationBits> {
State::new((self.generation() << 1) + 1)
}
fn null() -> Self {
Self(u8::MAX, PhantomData)
}
fn is_null(&self) -> bool {
self.0 == u8::MAX
}
}
impl<T, const GENERATION_BITS: usize> Copy for Id8<T, GENERATION_BITS> {}
impl<T, const GENERATION_BITS: usize> Clone for Id8<T, GENERATION_BITS> {
fn clone(&self) -> Self {
Self(self.0, PhantomData)
}
}
impl<T, const GENERATION_BITS: usize> fmt::Debug for Id8<T, GENERATION_BITS>
where
Self: IdTrait,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"Id {{ index: {}, generation {} }}",
self.index(),
self.generation(),
)
}
}
impl<T, const GENERATION_BITS: usize> Hash for Id8<T, GENERATION_BITS> {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
self.0.hash(state);
}
}
impl<T, const GENERATION_BITS: usize> PartialEq for Id8<T, GENERATION_BITS> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T, const GENERATION_BITS: usize> Eq for Id8<T, GENERATION_BITS> {}
#[test]
fn test_id_basics() {
let id64 = crate::Id::<()>::new(42, 99);
assert_eq!(id64.index(), 42);
assert_eq!(id64.generation(), 99);
let id32 = Id32::<(), 10>::new(42, 99);
assert_eq!(id32.index(), 42);
assert_eq!(id32.generation(), 99);
let id8 = Id8::<(), 4>::new(7, 15);
assert_eq!(id8.index(), 7);
assert_eq!(id8.generation(), 15);
}
}