use std::fmt;
use std::hash::Hash;
use std::hash::Hasher;
use std::ops::Deref;
#[repr(C)]
#[derive(Copy, Clone)]
pub struct GenerationalIndex<I: Copy + Into<usize> = usize> {
index: I,
#[cfg(debug_assertions)]
generation: usize,
}
impl Default for GenerationalIndex<usize> {
fn default() -> Self {
GenerationalIndex {
index: 0,
#[cfg(debug_assertions)]
generation: usize::MAX,
}
}
}
impl<I: Copy + Into<usize>> Deref for GenerationalIndex<I> {
type Target = I;
fn deref(&self) -> &Self::Target {
&self.index
}
}
impl<I: Copy + Into<usize>> GenerationalIndex<I> {
#[cfg(debug_assertions)]
fn new(index: I, generation: usize) -> Self {
Self { index, generation }
}
#[cfg(not(debug_assertions))]
fn new(index: I) -> Self {
Self { index }
}
}
#[derive(Clone, Debug, Default)]
pub struct GenerationCounter {
#[cfg(debug_assertions)]
current_generation: Vec<usize>,
}
impl GenerationCounter {
pub fn new() -> Self {
#[cfg(debug_assertions)]
{
Self {
current_generation: Vec::new(),
}
}
#[cfg(not(debug_assertions))]
Self {}
}
}
impl GenerationCounter {
pub fn create_index<I>(&mut self, index: I) -> GenerationalIndex<I>
where
I: Copy + Into<usize>,
{
#[cfg(debug_assertions)]
{
let generation = if self.current_generation.len() <= index.into() {
self.current_generation.resize(index.into() + 1, 0);
0
} else {
let generation = &mut self.current_generation[index.into()];
*generation = generation.wrapping_add(1);
*generation
};
GenerationalIndex::new(index, generation)
}
#[cfg(not(debug_assertions))]
{
GenerationalIndex::new(index)
}
}
pub fn recall_index<I>(&self, index: I) -> GenerationalIndex<I>
where
I: Copy + Into<usize>,
{
#[cfg(debug_assertions)]
{
GenerationalIndex::new(index, self.current_generation[index.into()])
}
#[cfg(not(debug_assertions))]
{
GenerationalIndex::new(index)
}
}
pub fn get_index<I>(&self, index: GenerationalIndex<I>) -> I
where
I: Copy + Into<usize> + fmt::Debug,
{
#[cfg(debug_assertions)]
{
if self.current_generation[index.index.into()] != index.generation {
panic!("Attempting to access an invalid index: {index:?}");
}
}
index.index
}
}
impl<I> PartialEq for GenerationalIndex<I>
where
I: Copy + Into<usize> + Eq,
{
fn eq(&self, other: &Self) -> bool {
#[cfg(debug_assertions)]
{
if self.generation == usize::MAX || other.generation == usize::MAX {
return false;
}
debug_assert_eq!(
self.generation, other.generation,
"Comparing indices of different generations"
);
}
self.index == other.index
}
}
impl<I> Eq for GenerationalIndex<I> where I: Copy + Into<usize> + Eq {}
impl<I> PartialOrd for GenerationalIndex<I>
where
I: Copy + Into<usize> + PartialOrd + Eq,
{
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
#[cfg(debug_assertions)]
debug_assert_eq!(
self.generation, other.generation,
"Comparing indices of different generations"
);
self.index.partial_cmp(&other.index)
}
}
impl<I> Ord for GenerationalIndex<I>
where
I: Copy + Into<usize> + Eq + Ord,
{
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
#[cfg(debug_assertions)]
debug_assert_eq!(
self.generation, other.generation,
"Comparing indices of different generations"
);
self.index.cmp(&other.index)
}
}
impl<I> Hash for GenerationalIndex<I>
where
I: Copy + Into<usize> + Hash,
{
fn hash<H: Hasher>(&self, state: &mut H) {
self.index.hash(state);
}
}
impl<I> fmt::Debug for GenerationalIndex<I>
where
I: Copy + Into<usize> + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(debug_assertions)]
{
write!(
f,
"GenerationalIndex(index: {:?}, generation: {})",
self.index, self.generation
)
}
#[cfg(not(debug_assertions))]
{
write!(f, "GenerationalIndex(index: {:?})", self.index)
}
}
}
impl fmt::Display for GenerationalIndex<usize> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.index)
}
}
#[cfg(test)]
mod tests {
#[cfg(debug_assertions)]
use crate::GenerationCounter;
#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn test_generational_index_equality() {
let mut counter = GenerationCounter::new();
let idx1 = counter.create_index(42usize);
let idx2 = counter.create_index(42usize);
let idx4 = counter.create_index(43usize);
let idx3 = counter.recall_index(42usize);
assert_ne!(idx1, idx4);
assert_eq!(idx2, idx3);
assert_eq!(idx1, idx2);
}
}