use std::num::NonZeroU32;
use petgraph::graph::{EdgeIndex, NodeIndex};
pub(crate) const FIRST_GENERATION: NonZeroU32 = NonZeroU32::MIN;
#[inline]
pub(crate) fn next_generation(generation: NonZeroU32) -> NonZeroU32 {
generation.checked_add(1).unwrap_or(FIRST_GENERATION)
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct VertexHandle {
slot: u32,
generation: NonZeroU32,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct EdgeHandle {
slot: u32,
generation: NonZeroU32,
}
impl VertexHandle {
#[inline]
pub(crate) fn new(index: NodeIndex, generation: NonZeroU32) -> Self {
Self {
slot: index.index() as u32,
generation,
}
}
#[inline]
pub(crate) fn index(self) -> NodeIndex {
NodeIndex::new(self.slot as usize)
}
#[inline]
pub(crate) fn generation(self) -> NonZeroU32 {
self.generation
}
#[cfg(test)]
pub(crate) fn for_test(slot: u32) -> Self {
Self {
slot,
generation: FIRST_GENERATION,
}
}
}
impl EdgeHandle {
#[inline]
pub(crate) fn new(index: EdgeIndex, generation: NonZeroU32) -> Self {
Self {
slot: index.index() as u32,
generation,
}
}
#[inline]
pub(crate) fn index(self) -> EdgeIndex {
EdgeIndex::new(self.slot as usize)
}
#[inline]
pub(crate) fn generation(self) -> NonZeroU32 {
self.generation
}
#[cfg(test)]
pub(crate) fn for_test(slot: u32) -> Self {
Self {
slot,
generation: FIRST_GENERATION,
}
}
}
impl std::fmt::Debug for VertexHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "v[{}#{}]", self.slot, self.generation)
}
}
impl std::fmt::Debug for EdgeHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "e[{}#{}]", self.slot, self.generation)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct EdgeGenerations {
pub(crate) own: NonZeroU32,
pub(crate) source: NonZeroU32,
pub(crate) target: NonZeroU32,
}
#[derive(Debug, Clone, Default)]
pub(crate) struct Generations {
slots: Vec<NonZeroU32>,
}
impl Generations {
#[inline]
pub(crate) fn current(&mut self, slot: usize) -> NonZeroU32 {
if slot >= self.slots.len() {
self.slots.resize(slot + 1, FIRST_GENERATION);
}
self.slots[slot]
}
#[inline]
pub(crate) fn bump(&mut self, slot: usize) {
if let Some(generation) = self.slots.get_mut(slot) {
*generation = next_generation(*generation);
}
}
pub(crate) fn heap_bytes(&self) -> u64 {
self.slots.capacity() as u64 * std::mem::size_of::<NonZeroU32>() as u64
}
#[inline]
pub(crate) fn shrink_to_fit(&mut self) {
self.slots.shrink_to_fit();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn generation_wraps_and_skips_zero() {
let max = NonZeroU32::new(u32::MAX).expect("non-zero");
assert_eq!(next_generation(max), FIRST_GENERATION);
assert_eq!(next_generation(FIRST_GENERATION).get(), 2);
}
#[test]
fn handles_are_eight_bytes_with_a_niche() {
assert_eq!(std::mem::size_of::<VertexHandle>(), 8);
assert_eq!(std::mem::size_of::<Option<VertexHandle>>(), 8);
assert_eq!(std::mem::size_of::<EdgeHandle>(), 8);
}
#[test]
fn debug_is_slot_and_generation() {
let v = VertexHandle::new(NodeIndex::new(3), NonZeroU32::new(2).expect("non-zero"));
assert_eq!(format!("{v:?}"), "v[3#2]");
let e = EdgeHandle::new(EdgeIndex::new(0), FIRST_GENERATION);
assert_eq!(format!("{e:?}"), "e[0#1]");
}
#[test]
fn bump_changes_the_generation_of_a_slot() {
let mut generations = Generations::default();
let first = generations.current(5);
generations.bump(5);
assert_ne!(generations.current(5), first);
assert_eq!(generations.current(0), FIRST_GENERATION);
}
}