Skip to main content

mtb_entity_slab/
policy.rs

1use crate::{
2    bitalloc::{BitAlloc, IBitAlloc, SummaryAlloc},
3    chunk::Unit,
4    gen_index::GenIndex,
5};
6use std::ops::IndexMut;
7
8/// Errors that can occur when working with slice pointers.
9///
10/// This is used in pointer ID validation in `EntityAlloc`.
11#[derive(Debug)]
12pub enum SlicePtrErr {
13    /// The pointer is out of the valid address range of the slice.
14    OutOfRange,
15
16    /// The pointer is not aligned with the start of any unit in the slice.
17    NotAlignedWith,
18}
19impl std::fmt::Display for SlicePtrErr {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        let msg = match self {
22            SlicePtrErr::OutOfRange => "Pointer is out of the valid address range of the slice.",
23            SlicePtrErr::NotAlignedWith => {
24                "Pointer is not aligned with the start of any unit in the slice."
25            }
26        };
27        f.write_str(msg)
28    }
29}
30impl std::error::Error for SlicePtrErr {}
31
32/// Result type for operations involving slice pointers.
33pub type SlicePtrRes<T = ()> = Result<T, SlicePtrErr>;
34
35pub trait IUnitArray<E>: Sized + IndexMut<usize, Output = Unit<E>> {
36    const LEN: usize;
37    unsafe fn boxed_uninit() -> Box<Self>;
38    fn indexof_unit_ptr(&self, ptr: *const Unit<E>) -> Result<usize, SlicePtrErr>;
39}
40impl<E, const N: usize> IUnitArray<E> for [Unit<E>; N] {
41    const LEN: usize = N;
42
43    unsafe fn boxed_uninit() -> Box<Self> {
44        let mut ret: Box<Self> = unsafe { Box::new_uninit().assume_init() };
45        let init_id = initial_id::initial_id();
46        for i in 0..Self::LEN {
47            ret[i].indexed = init_id;
48        }
49        ret
50    }
51
52    fn indexof_unit_ptr(&self, ptr: *const Unit<E>) -> Result<usize, SlicePtrErr> {
53        use std::ops::Range;
54        let unit_size = std::mem::size_of::<Unit<E>>();
55        let Range { start, end } = self.as_ptr_range();
56        let start_addr = start as usize;
57        let end_addr = end as usize;
58        let ptr_addr = ptr as usize;
59        if ptr_addr < start_addr || ptr_addr >= end_addr {
60            return Err(SlicePtrErr::OutOfRange);
61        }
62        let offset = ptr_addr - start_addr;
63        if !offset.is_multiple_of(unit_size) {
64            return Err(SlicePtrErr::NotAlignedWith);
65        }
66        Ok(offset / unit_size)
67    }
68}
69
70mod initial_id {
71    use crate::gen_index::GenIndex;
72
73    #[cfg(feature = "random-generation")]
74    pub(super) fn initial_id() -> GenIndex {
75        let generation: u16 = fastrand::u16(..);
76        GenIndex::compose(GenIndex::NULL_INDEX, generation)
77    }
78    #[cfg(not(feature = "random-generation"))]
79    pub(super) const fn initial_id() -> GenIndex {
80        GenIndex::compose(GenIndex::NULL_INDEX, 0)
81    }
82}
83
84/// Entity allocation policy trait.
85///
86/// You are NOT ALLOWED to implement this trait for your own types.
87/// You can only select the following pre-defined policies:
88///
89/// - `AllocPolicy128` - Chunk size of 128.
90/// - `AllocPolicy256` - Chunk size of 256.
91/// - `AllocPolicy512` - Chunk size of 512.
92/// - `AllocPolicy1024` - Chunk size of 1024.
93/// - `AllocPolicy2048` - Chunk size of 2048.
94/// - `AllocPolicy4096` - Chunk size of 4096.
95///
96/// Entity 分配策略接口.
97///
98/// 你不允许为自己的类型实现此接口. 你只能选择以下预定义的策略:
99///
100/// - `AllocPolicy128` - 块大小为 128.
101/// - `AllocPolicy256` - 块大小为 256.
102/// - `AllocPolicy512` - 块大小为 512.
103/// - `AllocPolicy1024` - 块大小为 1024.
104/// - `AllocPolicy2048` - 块大小为 2048.
105/// - `AllocPolicy4096` - 块大小为 4096.
106pub trait IAllocPolicy: 'static {
107    /// Chunk size log2 (e.g., 7 for 128, 8 for 256, etc.)
108    const CHUNK_SIZE_LOG2: usize;
109
110    /// Chunk size (e.g., 128, 256, etc.)
111    const CHUNK_SIZE: usize;
112
113    /// Bitset length (number of u64 units in the bitset)
114    const BITSET_LEN: usize;
115
116    /// Unit ID mask, usually CHUNK_SIZE - 1
117    const UNIT_ID_MASK: usize;
118
119    /// Bit allocator type. `MTB::Entity` chooses different bit allocators
120    /// for different chunk sizes.
121    type BitAlloc: IBitAlloc;
122
123    /// Unit array type for the chunk.
124    type Units<E>: IUnitArray<E>;
125
126    /// Compose a `GenIndex` from chunk ID, unit ID, and generation.
127    fn compose(chunk: u32, unit: u16, generation: u16) -> GenIndex {
128        let chunk_id_shift = Self::CHUNK_SIZE_LOG2;
129        let unit_id_mask = Self::UNIT_ID_MASK;
130        let chunk_id = chunk as u64;
131        let unit_id = (unit as u64) & (unit_id_mask as u64);
132        let generation = generation as u64;
133        let composed = (chunk_id << chunk_id_shift) | unit_id | (generation << GenIndex::GEN_SHIFT);
134        GenIndex(composed)
135    }
136    /// Extract chunk ID from a `GenIndex`.
137    fn chunk(index: GenIndex) -> u32 {
138        let chunk_id_shift = Self::CHUNK_SIZE_LOG2;
139        (index.0 >> chunk_id_shift) as u32
140    }
141    /// Extract unit ID from a `GenIndex`.
142    fn unit(index: GenIndex) -> u16 {
143        let unit_id_mask = Self::UNIT_ID_MASK as u64;
144        (index.0 & unit_id_mask) as u16
145    }
146
147    /// Tear down a `GenIndex` into chunk ID, unit ID, and generation.
148    fn tear(index: GenIndex) -> (u32, u16, u16) {
149        let chunk = Self::chunk(index);
150        let unit = Self::unit(index);
151        let generation = index.generation();
152        (chunk, unit, generation)
153    }
154}
155pub(crate) trait IAllocPolicyPrivate: IAllocPolicy {
156    /// # Safety
157    ///
158    /// This function returns a boxed array of uninitialized units.
159    unsafe fn unit_array<E>() -> Box<Self::Units<E>> {
160        unsafe { Self::Units::boxed_uninit() }
161    }
162}
163impl<T: IAllocPolicy> IAllocPolicyPrivate for T {}
164
165macro_rules! define_entity_alloc_policy {
166    ($name:ident, $chunk_size_log2:expr, $bit_alloc:ty, $doc:expr) => {
167        #[doc = $doc]
168        pub struct $name;
169
170        impl IAllocPolicy for $name {
171            const CHUNK_SIZE_LOG2: usize = $chunk_size_log2;
172            const CHUNK_SIZE: usize = 1 << $chunk_size_log2;
173            const BITSET_LEN: usize = Self::CHUNK_SIZE / 64;
174            const UNIT_ID_MASK: usize = (1 << Self::CHUNK_SIZE_LOG2) - 1;
175
176            type BitAlloc = $bit_alloc;
177            type Units<E> = [Unit<E>; Self::CHUNK_SIZE];
178        }
179    };
180}
181
182define_entity_alloc_policy! {AllocPolicy128,  7,  BitAlloc<2>, "Entity allocation policy for chunks of size 128."}
183define_entity_alloc_policy! {AllocPolicy256,  8,  BitAlloc<4>, "Entity allocation policy for chunks of size 256."}
184define_entity_alloc_policy! {AllocPolicy512,  9,  BitAlloc<8>, "Entity allocation policy for chunks of size 512."}
185define_entity_alloc_policy! {AllocPolicy1024, 10, SummaryAlloc<16>, "Entity allocation policy for chunks of size 1024."}
186define_entity_alloc_policy! {AllocPolicy2048, 11, SummaryAlloc<32>, "Entity allocation policy for chunks of size 2048."}
187define_entity_alloc_policy! {AllocPolicy4096, 12, SummaryAlloc<64>, "Entity allocation policy for chunks of size 4096."}