Skip to main content

mtb_entity_slab/
gen_index.rs

1/// Generation + index combined identifier used by the allocator.
2///
3/// The value stores a 16-bit generation in the high bits and a platform-sized
4/// real index in the low bits. Concretely this crate places the generation in
5/// the upper 16 bits and the real index in the lower 48 bits of the `u64`
6/// storage. `GenIndex` is a compact handle that allows fast comparison while
7/// also providing a generation counter to detect use-after-free or stale
8/// references.
9///
10/// Key points:
11/// - Upper 16 bits: generation - to differentiate entities at the same slot across different lifetimes.
12/// - Lower bits: real index - the actual index (48 bits on 64-bit platforms)
13/// - `is_null()` checks if the index is a special null value (`NULL_INDEX`).
14/// - `to_null()` replaces the real index with `NULL_INDEX`, preserving generation info.
15/// - 使用 `generation_inc()` 在释放并重新分配同一槽位时递增 generation,避免 ABA 问题。
16///
17/// 注意:真实索引必须小于或等于 `INDEX_MASK`,否则行为未定义;该类型提供了 `compose`
18/// 和 `replace_index` 等安全构造器/替换方法。
19///
20/// 用例示例(简要):
21/// ```ignore
22/// let idx = GenIndex::compose(1234, 1);
23/// let bumped = idx.generation_inc();
24/// assert!(!idx.generation_match(bumped));
25/// ```
26#[repr(transparent)]
27#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
28pub struct GenIndex(pub u64);
29impl From<u64> for GenIndex {
30    fn from(value: u64) -> Self {
31        GenIndex(value)
32    }
33}
34impl From<GenIndex> for u64 {
35    fn from(value: GenIndex) -> Self {
36        value.0
37    }
38}
39impl GenIndex {
40    #[cfg(not(any(target_pointer_width = "64", target_pointer_width = "32")))]
41    compile_error!("MTB::Entity only supports 32-bit and 64-bit platform.");
42
43    /// Bit shift amount for generation bits. ( = 48 )
44    pub const GEN_SHIFT: u32 = 48;
45
46    /// Bit mask for generation bits. ( = 0xFFFF000000000000 )
47    pub const GEN_MASK: u64 = 0xFFFF0000_00000000;
48
49    /// Bit mask for real index bits.
50    #[cfg(target_pointer_width = "64")]
51    pub const INDEX_MASK: u64 = 0x0000FFFF_FFFFFFFF;
52    #[cfg(target_pointer_width = "32")]
53    pub const INDEX_MASK: u64 = 0x0000_0000_FFFF_FFFF;
54
55    /// Special null index value for real index portion.
56    #[cfg(target_pointer_width = "64")]
57    pub const NULL_INDEX: usize = 0x0000FFFF_FFFFFFFF;
58    #[cfg(target_pointer_width = "32")]
59    pub const NULL_INDEX: usize = 0xFFFF_FFFF;
60
61    /// Compose a `GenIndex` from a raw `real_index` and `generation`.
62    ///
63    /// 构造函数:从真实索引和世代值组合出 `GenIndex`。`real_index` 会被掩码截断,
64    /// 因此传入值应保证在 `INDEX_MASK` 范围内以避免信息丢失。
65    pub const fn compose(real_index: usize, generation: u16) -> Self {
66        let generation = generation as u64;
67        let real_index = real_index as u64;
68        let composed = (real_index & Self::INDEX_MASK) | (generation << Self::GEN_SHIFT);
69        GenIndex(composed)
70    }
71
72    /// Return the generation (high 16 bits).
73    ///
74    /// 返回世代号(高 16 位)。
75    pub const fn generation(self) -> u16 {
76        (self.0 >> Self::GEN_SHIFT) as u16
77    }
78
79    /// Return the real index (low bits masked by `INDEX_MASK`).
80    ///
81    /// 返回真实索引(低位,已掩码)。
82    pub const fn real_index(self) -> usize {
83        (self.0 & Self::INDEX_MASK) as usize
84    }
85    /// Decompose into `(real_index, generation)` tuple.
86    ///
87    /// 拆分为 `(真实索引, 世代)`。
88    pub const fn tear(self) -> (usize, u16) {
89        (self.real_index(), self.generation())
90    }
91
92    /// Return a new `GenIndex` with generation incremented by one.
93    ///
94    /// 世代递增(使用 wrapping add),用于在槽位复用时更新世代号以避免允许旧引用访问。
95    pub const fn generation_inc(self) -> Self {
96        let generation_delta = 1u64 << Self::GEN_SHIFT;
97        GenIndex(self.0.wrapping_add(generation_delta))
98    }
99
100    /// Check whether two `GenIndex` values have the same generation.
101    ///
102    /// 比较两个索引的世代是否相同(不关心真实索引部分)。
103    pub const fn generation_match(self, other: Self) -> bool {
104        self.generation() == other.generation()
105    }
106
107    /// Replace the real-index portion while preserving the generation bits.
108    ///
109    /// 替换真实索引部分,同时保留世代位。这在需要重映射索引但保留
110    /// 当前世代信息时非常有用。`new_index` 将被掩码截断以适配位宽。
111    pub const fn replace_index(self, new_index: usize) -> Self {
112        let gen_part = self.0 & Self::GEN_MASK;
113        let new_index = new_index as u64;
114        GenIndex(gen_part | (new_index & Self::INDEX_MASK))
115    }
116
117    /// Check whether this `GenIndex` represents a null sentinel index.
118    ///
119    /// 判定是否为特殊空索引(`NULL_INDEX`),通常用于表示无效/空引用。
120    pub const fn is_null(self) -> bool {
121        self.real_index() == Self::NULL_INDEX
122    }
123
124    /// Convert this `GenIndex` into a null-index while preserving generation bits.
125    ///
126    /// 将真实索引替换为 `NULL_INDEX`,保留世代信息。
127    pub const fn to_null(self) -> Self {
128        self.replace_index(Self::NULL_INDEX)
129    }
130
131    /// Create a canonical null `GenIndex` with generation `0`.
132    ///
133    /// 返回一个基本的空索引(世代 = 0)。
134    pub const fn new_basic_null() -> Self {
135        GenIndex::compose(Self::NULL_INDEX, 0)
136    }
137}