mtb_entity_slab/
policy.rs1use crate::{
2 bitalloc::{BitAlloc, IBitAlloc, SummaryAlloc},
3 chunk::Unit,
4 gen_index::GenIndex,
5};
6use std::ops::IndexMut;
7
8#[derive(Debug)]
12pub enum SlicePtrErr {
13 OutOfRange,
15
16 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
32pub 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
84pub trait IAllocPolicy: 'static {
107 const CHUNK_SIZE_LOG2: usize;
109
110 const CHUNK_SIZE: usize;
112
113 const BITSET_LEN: usize;
115
116 const UNIT_ID_MASK: usize;
118
119 type BitAlloc: IBitAlloc;
122
123 type Units<E>: IUnitArray<E>;
125
126 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 fn chunk(index: GenIndex) -> u32 {
138 let chunk_id_shift = Self::CHUNK_SIZE_LOG2;
139 (index.0 >> chunk_id_shift) as u32
140 }
141 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 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 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."}