use std::{alloc::Layout, mem, ptr::NonNull};
use crate::{
block::{Block, BLOCK_HEADER_SIZE, MIN_BLOCK_SIZE},
header::Header,
list::LinkedList,
platform,
};
pub(crate) const REGION_HEADER_SIZE: usize = mem::size_of::<Header<Region>>();
pub(crate) struct Region {
pub blocks: LinkedList<Block>,
pub size: usize,
}
impl Header<Region> {
#[inline]
pub unsafe fn first_block(&self) -> NonNull<Header<Block>> {
self.data.blocks.first().unwrap_unchecked()
}
#[inline]
pub fn size(&self) -> usize {
self.data.size
}
#[inline]
pub fn total_size(&self) -> usize {
REGION_HEADER_SIZE + self.data.size
}
#[inline]
pub fn num_blocks(&self) -> usize {
self.data.blocks.len()
}
}
pub(crate) fn determine_region_length(size: usize) -> usize {
let total_size = REGION_HEADER_SIZE + BLOCK_HEADER_SIZE + size;
let mut length = Layout::from_size_align(total_size, platform::page_size())
.unwrap()
.pad_to_align()
.size();
if total_size < length && total_size + BLOCK_HEADER_SIZE + MIN_BLOCK_SIZE > length {
length += platform::page_size();
}
length
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{alignment::POINTER_SIZE, platform::PAGE_SIZE};
#[test]
fn region_length() {
unsafe {
assert_eq!(determine_region_length(POINTER_SIZE), platform::page_size());
assert_eq!(determine_region_length(PAGE_SIZE / 2), PAGE_SIZE);
for i in 1..=100 {
assert_eq!(determine_region_length(PAGE_SIZE * i), PAGE_SIZE * (i + 1));
}
let exact_remaining_space = PAGE_SIZE - REGION_HEADER_SIZE - BLOCK_HEADER_SIZE;
assert_eq!(determine_region_length(exact_remaining_space), PAGE_SIZE);
let enough_space_for_minimum_block_at_the_end =
PAGE_SIZE - REGION_HEADER_SIZE - 2 * BLOCK_HEADER_SIZE - MIN_BLOCK_SIZE;
assert_eq!(
determine_region_length(enough_space_for_minimum_block_at_the_end),
PAGE_SIZE
);
let not_enough_space_for_minimum_block_at_the_end =
PAGE_SIZE - REGION_HEADER_SIZE - 2 * BLOCK_HEADER_SIZE - MIN_BLOCK_SIZE
+ POINTER_SIZE;
assert_eq!(
determine_region_length(not_enough_space_for_minimum_block_at_the_end),
2 * PAGE_SIZE
);
}
}
}