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}