use core::marker::PhantomData;
use either::Either;
use crate::{
alignment::is_aligned,
chunks::FreeChunkPtr,
smallest_type_which_has_at_least_n_bits::{
ContainsAlignmentsBitmapTrait, SmallestTypeWhichHasAtLeastNBits,
SmallestTypeWhichHasAtLeastNBitsStruct, SmallestTypeWhichHasAtLeastNBitsTrait,
},
HEADER_SIZE, MIN_ALIGNMENT, MIN_FREE_CHUNK_SIZE_INCLUDING_HEADER,
};
pub const DEFAULT_SMALLBINS_AMOUNT: usize = 20;
pub const DEFAULT_ALIGNMENT_SUB_BINS_AMOUNT: usize = 8;
pub const MIN_ALIGNMENT_LOG2: usize = unsafe { log2_of_power_of_2(MIN_ALIGNMENT) };
const SMALLEST_SMALLBIN_SIZE: usize = MIN_FREE_CHUNK_SIZE_INCLUDING_HEADER - HEADER_SIZE;
const OPTIMAL_SMALLBIN_LOOKAHEAD: usize = MIN_FREE_CHUNK_SIZE_INCLUDING_HEADER / MIN_ALIGNMENT;
pub const unsafe fn log2_of_power_of_2(x: usize) -> usize {
x.trailing_zeros() as usize
}
#[derive(Debug)]
pub struct SmallBins<const SMALLBINS_AMOUNT: usize, const ALIGNMENT_SUB_BINS_AMOUNT: usize>
where
SmallestTypeWhichHasAtLeastNBitsStruct<ALIGNMENT_SUB_BINS_AMOUNT>:
SmallestTypeWhichHasAtLeastNBitsTrait,
{
pub(crate) small_bins: [SmallBin<ALIGNMENT_SUB_BINS_AMOUNT>; SMALLBINS_AMOUNT],
}
impl<const SMALLBINS_AMOUNT: usize, const ALIGNMENT_SUB_BINS_AMOUNT: usize>
SmallBins<SMALLBINS_AMOUNT, ALIGNMENT_SUB_BINS_AMOUNT>
where
SmallestTypeWhichHasAtLeastNBitsStruct<ALIGNMENT_SUB_BINS_AMOUNT>:
SmallestTypeWhichHasAtLeastNBitsTrait,
{
const LARGEST_SMALLBIN_SIZE: usize = if SMALLBINS_AMOUNT == 0 {
0
} else {
SMALLEST_SMALLBIN_SIZE + (SMALLBINS_AMOUNT - 1) * MIN_ALIGNMENT
};
pub const MAX_ALIGNMENT_INDEX: usize = ALIGNMENT_SUB_BINS_AMOUNT - 1;
pub const MAX_SPECIFIC_ALIGNMENT: usize =
1 << (Self::MAX_SPECIFIC_ALIGNMENT_INDEX + MIN_ALIGNMENT_LOG2);
pub const MAX_SPECIFIC_ALIGNMENT_INDEX: usize = ALIGNMENT_SUB_BINS_AMOUNT - 1 - 1;
pub const fn new() -> Self {
Self {
small_bins: [SmallBin::new(); SMALLBINS_AMOUNT],
}
}
pub unsafe fn is_smallbin_size(size: usize) -> bool {
size <= Self::LARGEST_SMALLBIN_SIZE
}
pub unsafe fn smallbin_index(size: usize) -> Option<usize> {
if size > Self::LARGEST_SMALLBIN_SIZE {
return None;
}
Some(Self::smallbin_index_unchecked(size))
}
pub unsafe fn smallbin_index_unchecked(size: usize) -> usize {
(size - SMALLEST_SMALLBIN_SIZE) / MIN_ALIGNMENT
}
pub unsafe fn alignment_index(alignment: usize) -> usize {
if alignment > Self::MAX_SPECIFIC_ALIGNMENT {
return Self::MAX_ALIGNMENT_INDEX;
}
log2_of_power_of_2(alignment) - MIN_ALIGNMENT_LOG2
}
pub unsafe fn alignment_index_of_chunk_content_addr(chunk_content_addr: usize) -> usize {
let largest_power_of_2_that_divides_addr = chunk_content_addr.trailing_zeros() as usize;
let alignment_index = largest_power_of_2_that_divides_addr - MIN_ALIGNMENT_LOG2;
core::cmp::min(alignment_index, Self::MAX_ALIGNMENT_INDEX)
}
unsafe fn perfect_size_fit_smallbin_index(size: usize) -> usize {
(size - SMALLEST_SMALLBIN_SIZE) / MIN_ALIGNMENT
}
pub unsafe fn optimal_chunk(
&self,
size: usize,
alignment: usize,
alignment_index: usize,
) -> Option<FreeChunkPtr> {
let perfect_size_fit_smallbin_index = Self::perfect_size_fit_smallbin_index(size);
let used_smallbins_end_index = core::cmp::min(
perfect_size_fit_smallbin_index + OPTIMAL_SMALLBIN_LOOKAHEAD,
SMALLBINS_AMOUNT,
);
Self::get_first_aligned_chunk(
alignment,
alignment_index,
&self.small_bins[perfect_size_fit_smallbin_index..used_smallbins_end_index],
)
}
pub unsafe fn aligned_suboptimal_chunk(
&self,
size: usize,
alignment: usize,
alignment_index: usize,
) -> Option<AlignedSuboptimalChunk> {
let perfect_size_fit_smallbin_index = Self::perfect_size_fit_smallbin_index(size);
let optimal_smallbins_end = perfect_size_fit_smallbin_index + OPTIMAL_SMALLBIN_LOOKAHEAD;
if optimal_smallbins_end >= SMALLBINS_AMOUNT {
return None;
}
let mut chunk_ptr = Self::get_first_aligned_chunk(
alignment,
alignment_index,
&self.small_bins[optimal_smallbins_end..],
)?;
let end_padding = chunk_ptr.as_mut().size() - size;
Some(AlignedSuboptimalChunk {
chunk_ptr,
end_padding,
})
}
pub unsafe fn unaligned_suboptimal_chunks<'a>(
&'a self,
size: usize,
alignment: usize,
alignment_index: usize,
) -> Option<
Either<impl Iterator<Item = FreeChunkPtr> + 'a, impl Iterator<Item = FreeChunkPtr> + 'a>,
> {
let perfect_size_fit_smallbin_index = Self::perfect_size_fit_smallbin_index(size);
let optimal_smallbins_end = perfect_size_fit_smallbin_index + OPTIMAL_SMALLBIN_LOOKAHEAD;
if optimal_smallbins_end >= SMALLBINS_AMOUNT {
return None;
}
let smallbins_to_check = self.small_bins[optimal_smallbins_end..].iter();
if alignment_index > Self::MAX_SPECIFIC_ALIGNMENT_INDEX {
Some(Either::Left(
smallbins_to_check
.map(move |small_bin| {
small_bin.alignment_sub_bins[Self::MAX_ALIGNMENT_INDEX].chunks()
})
.flatten()
.filter(move |&(mut chunk_ptr)| {
let chunk = chunk_ptr.as_mut();
!is_aligned(chunk.content_addr(), alignment)
}),
))
} else {
Some(Either::Right(
smallbins_to_check
.map(move |small_bin| {
small_bin.alignment_sub_bins[..alignment_index].iter()
})
.flatten()
.filter_map(move |sub_bin| {
sub_bin.fd
}),
))
}
}
pub unsafe fn get_fd_and_bk_pointers_for_inserting_to_smallbin(
&mut self,
smallbin_index: usize,
alignment_index: usize,
) -> (Option<FreeChunkPtr>, *mut Option<FreeChunkPtr>) {
let smallbin = &mut self.small_bins[smallbin_index];
smallbin
.contains_alignments_bitmap
.set_contains_alignment(alignment_index);
let alignment_sub_bin = &mut smallbin.alignment_sub_bins[alignment_index];
(alignment_sub_bin.fd, &mut alignment_sub_bin.fd)
}
pub fn update_smallbin_after_removing_chunk_from_its_sub_bin(
&mut self,
smallbin_index: usize,
alignment_index: usize,
) {
let smallbin = &mut self.small_bins[smallbin_index];
if smallbin.alignment_sub_bins[alignment_index].fd.is_none() {
smallbin
.contains_alignments_bitmap
.unset_contains_alignment(alignment_index)
}
}
fn get_first_aligned_chunk(
alignment: usize,
alignment_index: usize,
smallbins: &[SmallBin<ALIGNMENT_SUB_BINS_AMOUNT>],
) -> Option<FreeChunkPtr> {
if alignment_index > Self::MAX_SPECIFIC_ALIGNMENT_INDEX {
smallbins
.iter()
.map(move |smallbin| {
smallbin.alignment_sub_bins[Self::MAX_ALIGNMENT_INDEX].chunks()
})
.flatten()
.filter(|&(mut chunk_ptr)| {
let chunk = unsafe { chunk_ptr.as_mut() };
unsafe { is_aligned(chunk.content_addr(), alignment) }
})
.next()
} else {
smallbins
.iter()
.filter(move |small_bin| {
small_bin
.contains_alignments_bitmap
.contains_aligment_greater_or_equal_to(alignment)
})
.map(move |smallbin| {
smallbin.alignment_sub_bins[alignment_index..]
.iter()
.filter_map(|sub_bin| {
sub_bin.fd
})
})
.flatten()
.next()
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct SmallBin<const ALIGNMENT_SUB_BINS_AMOUNT: usize>
where
SmallestTypeWhichHasAtLeastNBitsStruct<ALIGNMENT_SUB_BINS_AMOUNT>:
SmallestTypeWhichHasAtLeastNBitsTrait,
{
pub(crate) alignment_sub_bins: [AlignmentSubBin; ALIGNMENT_SUB_BINS_AMOUNT],
pub(crate) contains_alignments_bitmap: ContainsAlignmentsBitmap<ALIGNMENT_SUB_BINS_AMOUNT>,
}
impl<const ALIGNMENT_SUB_BINS_AMOUNT: usize> SmallBin<ALIGNMENT_SUB_BINS_AMOUNT>
where
SmallestTypeWhichHasAtLeastNBitsStruct<ALIGNMENT_SUB_BINS_AMOUNT>:
SmallestTypeWhichHasAtLeastNBitsTrait,
{
pub const fn new() -> Self {
Self {
alignment_sub_bins: [AlignmentSubBin::new(); ALIGNMENT_SUB_BINS_AMOUNT],
contains_alignments_bitmap: ContainsAlignmentsBitmap::new(),
}
}
}
impl<const ALIGNMENT_SUB_BINS_AMOUNT: usize> Default for SmallBin<ALIGNMENT_SUB_BINS_AMOUNT>
where
SmallestTypeWhichHasAtLeastNBitsStruct<ALIGNMENT_SUB_BINS_AMOUNT>:
SmallestTypeWhichHasAtLeastNBitsTrait,
{
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, Debug)]
pub struct AlignmentSubBin {
pub(crate) fd: Option<FreeChunkPtr>,
}
impl AlignmentSubBin {
pub const fn new() -> Self {
Self { fd: None }
}
pub fn chunks(&self) -> AlignmentSubBinChunks {
AlignmentSubBinChunks {
cur: self.fd,
phantom: PhantomData,
}
}
}
impl Default for AlignmentSubBin {
fn default() -> Self {
Self::new()
}
}
pub struct AlignmentSubBinChunks<'a> {
cur: Option<FreeChunkPtr>,
phantom: PhantomData<&'a ()>,
}
impl<'a> Iterator for AlignmentSubBinChunks<'a> {
type Item = FreeChunkPtr;
fn next(&mut self) -> Option<Self::Item> {
let mut cur = self.cur?;
self.cur = unsafe { cur.as_mut() }.fd;
Some(cur)
}
}
#[derive(Clone, Copy, Debug)]
pub struct ContainsAlignmentsBitmap<const ALIGNMENT_SUB_BINS_AMOUNT: usize>
where
SmallestTypeWhichHasAtLeastNBitsStruct<ALIGNMENT_SUB_BINS_AMOUNT>:
SmallestTypeWhichHasAtLeastNBitsTrait,
{
bitmap: SmallestTypeWhichHasAtLeastNBits<ALIGNMENT_SUB_BINS_AMOUNT>,
}
impl<const ALIGNMENT_SUB_BINS_AMOUNT: usize> ContainsAlignmentsBitmap<ALIGNMENT_SUB_BINS_AMOUNT>
where
SmallestTypeWhichHasAtLeastNBitsStruct<ALIGNMENT_SUB_BINS_AMOUNT>:
SmallestTypeWhichHasAtLeastNBitsTrait,
{
pub const fn new() -> Self {
Self {
bitmap: SmallestTypeWhichHasAtLeastNBits::<ALIGNMENT_SUB_BINS_AMOUNT>::ZERO,
}
}
pub fn contains_aligment_greater_or_equal_to(&self, alignment: usize) -> bool {
self.bitmap.to_usize() >= (alignment >> MIN_ALIGNMENT_LOG2)
}
pub fn set_contains_alignment(&mut self, alignment_index: usize) {
let alignment = 1 << alignment_index;
self.bitmap |=
SmallestTypeWhichHasAtLeastNBits::<ALIGNMENT_SUB_BINS_AMOUNT>::from_usize(alignment);
}
pub fn unset_contains_alignment(&mut self, alignment_index: usize) {
let alignment = 1 << alignment_index;
self.bitmap &=
!(SmallestTypeWhichHasAtLeastNBits::<ALIGNMENT_SUB_BINS_AMOUNT>::from_usize(alignment));
}
}
pub struct AlignedSuboptimalChunk {
pub chunk_ptr: FreeChunkPtr,
pub end_padding: usize,
}