mtb_entity_slab/
policy.rs1use crate::{
2 NULL_INDEXED_ID,
3 bitalloc::{BitAlloc, IBitAlloc, SummaryAlloc},
4 chunk::Unit,
5};
6use std::ops::IndexMut;
7
8#[derive(Debug)]
9pub enum SlicePtrError {
10 OutOfRange,
11 NotAlignedWith,
12}
13
14pub trait IUnitArray<E>: Sized + IndexMut<usize, Output = Unit<E>> {
15 const LEN: usize;
16 unsafe fn boxed_uninit() -> Box<Self>;
17 fn indexof_unit_ptr(&self, ptr: *const Unit<E>) -> Result<usize, SlicePtrError>;
18}
19impl<E, const N: usize> IUnitArray<E> for [Unit<E>; N] {
20 const LEN: usize = N;
21
22 unsafe fn boxed_uninit() -> Box<Self> {
23 let mut ret: Box<Self> = unsafe { Box::new_uninit().assume_init() };
24 for i in 0..Self::LEN {
25 ret[i].indexed_id = NULL_INDEXED_ID;
26 }
27 ret
28 }
29
30 fn indexof_unit_ptr(&self, ptr: *const Unit<E>) -> Result<usize, SlicePtrError> {
31 use std::ops::Range;
32 let unit_size = std::mem::size_of::<Unit<E>>();
33 let Range { start, end } = self.as_ptr_range();
34 let start_addr = start as usize;
35 let end_addr = end as usize;
36 let ptr_addr = ptr as usize;
37 if ptr_addr < start_addr || ptr_addr >= end_addr {
38 return Err(SlicePtrError::OutOfRange);
39 }
40 let offset = ptr_addr - start_addr;
41 if offset % unit_size != 0 {
42 return Err(SlicePtrError::NotAlignedWith);
43 }
44 Ok(offset / unit_size)
45 }
46}
47
48pub trait IAllocPolicy: 'static {
49 const CHUNK_SIZE_LOG2: usize;
50 const CHUNK_SIZE: usize;
51 const BITSET_LEN: usize;
52 const INDEXED_ID_PMASK: usize;
53
54 type BitAlloc: IBitAlloc;
55 type Units<E>: IUnitArray<E>;
56
57 unsafe fn unit_array<E>() -> Box<Self::Units<E>> {
58 unsafe { Self::Units::boxed_uninit() }
59 }
60 fn compose_indexed_id(chunk_id: u32, unit_id: u16) -> usize {
61 debug_assert!(
62 unit_id < Self::CHUNK_SIZE as u16,
63 "unit_id {unit_id} out of bounds {}",
64 Self::CHUNK_SIZE
65 );
66 let chunk_size_log2 = Self::CHUNK_SIZE_LOG2;
67 ((chunk_id as usize) << chunk_size_log2) | (unit_id as usize)
68 }
69 fn split_indexed_id(indexed_id: usize) -> (u32, u16) {
70 let chunk_size_log2 = Self::CHUNK_SIZE_LOG2;
71 let chunk_id = (indexed_id >> chunk_size_log2) as u32;
72 let unit_id = (indexed_id & ((1usize << chunk_size_log2) - 1)) as u16;
73 (chunk_id, unit_id)
74 }
75 fn unit_of_indexed_id(indexed_id: usize) -> u16 {
76 let chunk_size_log2 = Self::CHUNK_SIZE_LOG2;
77 (indexed_id & ((1usize << chunk_size_log2) - 1)) as u16
78 }
79 fn chunk_of_indexed_id(indexed_id: usize) -> u32 {
80 let chunk_size_log2 = Self::CHUNK_SIZE_LOG2;
81 (indexed_id >> chunk_size_log2) as u32
82 }
83}
84
85macro_rules! define_entity_alloc_policy {
86 ($name:ident, $chunk_size_log2:expr, $bit_alloc:ty, $doc:expr) => {
87 #[doc = $doc]
88 pub struct $name;
89
90 impl IAllocPolicy for $name {
91 const CHUNK_SIZE_LOG2: usize = $chunk_size_log2;
92 const CHUNK_SIZE: usize = 1 << $chunk_size_log2;
93 const BITSET_LEN: usize = Self::CHUNK_SIZE / 64;
94 const INDEXED_ID_PMASK: usize = (1 << Self::CHUNK_SIZE_LOG2) - 1;
95
96 type BitAlloc = $bit_alloc;
97 type Units<E> = [Unit<E>; Self::CHUNK_SIZE];
98 }
99 };
100}
101
102define_entity_alloc_policy! {AllocPolicy128, 7, BitAlloc<2>, "Entity allocation policy for chunks of size 128."}
103define_entity_alloc_policy! {AllocPolicy256, 8, BitAlloc<4>, "Entity allocation policy for chunks of size 256."}
104define_entity_alloc_policy! {AllocPolicy512, 9, BitAlloc<8>, "Entity allocation policy for chunks of size 512."}
105define_entity_alloc_policy! {AllocPolicy1024, 10, SummaryAlloc<16>, "Entity allocation policy for chunks of size 1024."}
106define_entity_alloc_policy! {AllocPolicy2048, 11, SummaryAlloc<32>, "Entity allocation policy for chunks of size 2048."}
107define_entity_alloc_policy! {AllocPolicy4096, 12, SummaryAlloc<64>, "Entity allocation policy for chunks of size 4096."}