mtb_entity_slab/
policy.rs

1use 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."}