mtb-entity-slab 0.2.4

Slab-style entity storage: stable IDs, internal mutability; not a full ECS.
Documentation
use std::num::{NonZeroU16, NonZeroU64};

/// Generation + index combined identifier used by the allocator.
///
/// The value stores a 16-bit generation in the low bits and a platform-sized
/// real index in the high bits. Concretely this crate places the generation in
/// the lower 16 bits and the real index in the upper bits of the `u64`
/// storage. `GenIndex` is a compact handle that allows fast comparison while
/// also providing a generation counter to detect use-after-free or stale
/// references.
///
/// Key points:
/// - Lower 16 bits: generation - to differentiate entities at the same slot across different lifetimes.
/// - Upper bits: real index - the actual index (48 bits on 64-bit platforms)
/// - `is_invalid()` checks if the index is a special invalid value (`INVALID_INDEX`).
/// - `to_invalid()` replaces the real index with `INVALID_INDEX`, preserving generation info.
/// - 使用 `generation_inc()` 在释放并重新分配同一槽位时递增 generation,避免 ABA 问题。
///
/// 注意:真实索引必须小于或等于 `INDEX_MASK`,否则行为未定义;该类型提供了 `compose`
/// 和 `replace_index` 等安全构造器/替换方法。
///
/// 用例示例(简要):
/// ```ignore
/// let idx = GenIndex::compose(1234, 1);
/// let bumped = idx.generation_inc();
/// assert!(!idx.generation_match(bumped));
/// ```
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GenIndex(pub NonZeroU64);
impl From<NonZeroU64> for GenIndex {
    fn from(value: NonZeroU64) -> Self {
        GenIndex(value)
    }
}
impl From<GenIndex> for NonZeroU64 {
    fn from(value: GenIndex) -> Self {
        value.0
    }
}
impl From<GenIndex> for u64 {
    fn from(value: GenIndex) -> Self {
        value.0.get()
    }
}
impl GenIndex {
    #[cfg(not(any(target_pointer_width = "64", target_pointer_width = "32")))]
    compile_error!("MTB::Entity only supports 32-bit and 64-bit platform.");

    /// Bit shift amount for generation bits. ( = 0 )
    pub const GEN_SHIFT: u32 = 0;

    /// Bit mask for generation bits. ( = 0xFFFF )
    pub const GEN_MASK: u64 = 0xFFFF;

    /// Generation value representing the first generation (1).
    /// Generation 0 is reserved to represent invalid indices.
    pub const GEN_1: NonZeroU16 = NonZeroU16::new(1).unwrap();

    /// Bit mask for real index bits.
    pub const INDEX_MASK: u64 = !Self::GEN_MASK;

    /// Bit shift amount for real index bits. ( = 16 )
    pub const INDEX_SHIFT: u32 = Self::GEN_SHIFT + 16;

    /// INVALID_INDEX is a special real index value (usize::MAX) used to
    /// represent an invalid or null reference.
    #[cfg(target_pointer_width = "32")]
    pub const INVALID_INDEX: usize = usize::MAX;

    /// On 64-bit platforms, we can use the full 48 bits for real index,
    /// so INVALID_INDEX is 0xFFFFFFFFFFFF.
    #[cfg(target_pointer_width = "64")]
    pub const INVALID_INDEX: usize = 0xFFFF_FFFF_FFFF;

    pub(crate) const fn do_generation_inc(gene: NonZeroU16) -> NonZeroU16 {
        let gene = gene.get();
        let gene = match gene.wrapping_add(1) {
            0 => 1, // Skip zero to maintain non-zero invariant
            other => other,
        };
        NonZeroU16::new(gene).expect("Generation must be non-zero")
    }

    /// Compose a `GenIndex` from a raw `real_index` and `generation`.
    ///
    /// 构造函数:从真实索引和世代值组合出 `GenIndex`。`real_index` 会被掩码截断,
    /// 因此传入值应保证在 `INDEX_MASK` 范围内以避免信息丢失。
    pub const fn compose(real_index: usize, generation: NonZeroU16) -> Self {
        let real_index = real_index as u64;
        let generation = generation.get() as u64;
        let value = real_index << Self::INDEX_SHIFT | generation;
        let value = NonZeroU64::new(value).expect("GenIndex value must be non-zero");
        GenIndex(value)
    }

    /// Return the generation.
    ///
    /// 返回世代号
    pub const fn generation(self) -> NonZeroU16 {
        let gene = (self.0.get() & Self::GEN_MASK) as u16;
        NonZeroU16::new(gene).expect("GenIndex generation must be non-zero")
    }

    /// Return the real index (high bits masked by `INDEX_MASK`).
    ///
    /// 返回真实索引(高位,已掩码)。
    pub const fn real_index(self) -> usize {
        ((self.0.get() & Self::INDEX_MASK) >> Self::INDEX_SHIFT) as usize
    }
    /// Decompose into `(real_index, generation)` tuple.
    ///
    /// 拆分为 `(真实索引, 世代)`。
    pub const fn tear(self) -> (usize, NonZeroU16) {
        (self.real_index(), self.generation())
    }

    /// Return a new `GenIndex` with generation incremented by one.
    ///
    /// 世代递增(使用 wrapping add),用于在槽位复用时更新世代号以避免允许旧引用访问。
    pub const fn generation_inc(self) -> Self {
        let (real_index, generation) = self.tear();
        let new_generation = Self::do_generation_inc(generation);
        Self::compose(real_index, new_generation)
    }

    /// Check whether two `GenIndex` values have the same generation.
    ///
    /// 比较两个索引的世代是否相同(不关心真实索引部分)。
    pub const fn generation_match(self, other: Self) -> bool {
        let lgene = self.0.get() & Self::GEN_MASK;
        let rgene = other.0.get() & Self::GEN_MASK;
        lgene == rgene
    }

    /// Replace the real-index portion while preserving the generation bits.
    ///
    /// 替换真实索引部分,同时保留世代位。这在需要重映射索引但保留
    /// 当前世代信息时非常有用。`new_index` 将被掩码截断以适配位宽。
    pub const fn replace_index(self, new_index: usize) -> Self {
        Self::compose(new_index, self.generation())
    }

    /// Check whether this `GenIndex` represents an invalid sentinel index.
    ///
    /// 判定是否为特殊无效索引(`INVALID_INDEX`),通常用于表示无效/空引用。
    pub const fn is_invalid(self) -> bool {
        self.real_index() == Self::INVALID_INDEX
    }

    /// Convert this `GenIndex` into an invalid-index while preserving generation bits.
    ///
    /// 将真实索引替换为 `INVALID_INDEX`,保留世代信息。
    pub const fn to_invalid(self) -> Self {
        self.replace_index(Self::INVALID_INDEX)
    }

    /// Create a canonical invalid `GenIndex` with generation `1`.
    ///
    /// 返回一个基本的无效索引(世代 = 1)。
    pub const fn new_basic_invalid() -> Self {
        GenIndex::compose(Self::INVALID_INDEX, Self::GEN_1)
    }
}