#![feature(ptr_internals)]
#![feature(allocator_api)]
#![feature(nonnull_slice_from_raw_parts)]
mod consts;
mod function;
mod structs;
use function::*;
use std::{
alloc::{self, GlobalAlloc},
cell::RefCell,
mem,
ptr::{self, null_mut, NonNull},
};
use structs::{AreaInfo, BlockHeader, FreeNode, TLSFChunk, TLSFRawHeader, TLSFRootChunk};
extern crate arrayvec;
use arrayvec::ArrayVec;
extern crate spin;
use spin::Mutex;
struct RootPool {
memory: TLSFRootChunk,
}
impl RootPool {
fn tlsf_header(&self) -> &mut TLSFRawHeader {
unsafe {
(self.memory.ptr().as_ptr() as *mut TLSFRawHeader)
.as_mut()
.unwrap()
}
}
pub fn from(requested_size: usize) -> Option<Self> {
const MINIMUM_REQUIRED_SIZE: usize = TLSFRawHeader::get_aligned_size()
+ (BlockHeader::get_aligned_size() * 3)
+ AreaInfo::get_aligned_size();
if requested_size < MINIMUM_REQUIRED_SIZE {
return None;
}
let new_chunk = TLSFRootChunk::new(requested_size)?;
let first_block = unsafe {
let offset = TLSFRawHeader::get_aligned_size() as isize;
(new_chunk.ptr().as_ptr().offset(offset) as *mut BlockHeader)
.as_mut()
.unwrap()
.next_block_as_mut()
};
let pool = Self { memory: new_chunk };
let tlsf_header = pool.tlsf_header();
tlsf_header.maximum_memory_size = first_block.buffer_size_with_header();
tlsf_header.used_memory_size += tlsf_header.maximum_memory_size;
unsafe {
pool.dealloc(
first_block.buffer_as_ptr().unwrap().as_ptr(),
alloc::Layout::new::<u8>(),
);
}
Some(pool)
}
}
unsafe impl alloc::GlobalAlloc for RootPool {
unsafe fn alloc(&self, layout: alloc::Layout) -> *mut u8 {
let aligned_size = calculate_allocation_searching_size(layout.size());
let tlsf_header = self.tlsf_header();
let mapping_indices = match tlsf_header.find_suitable_indices(aligned_size) {
None => return null_mut(),
Some(mapping_indices) => mapping_indices,
};
let suitable_block = match tlsf_header.extract_root_block(mapping_indices) {
None => return null_mut(),
Some(mut suitable_block) => suitable_block.as_mut(),
};
assert!(
suitable_block.buffer_size() >= aligned_size,
"Buffer size of retrieved block must be larger or equal to aligned size."
);
const BLOCK_SIZE: usize = BlockHeader::get_aligned_size() + mem::size_of::<FreeNode>();
let remained_size = suitable_block.buffer_size() - aligned_size;
if remained_size < BLOCK_SIZE {
suitable_block.next_block_as_mut().set_previous_freed(false);
} else {
let new_buffer_size = remained_size - BlockHeader::get_aligned_size();
let new_block = {
let new_block = suitable_block
.buffer_pointer_as::<u8>()
.offset(aligned_size as isize);
ptr::write(
new_block as *mut _,
BlockHeader::new(new_buffer_size, true, false, None),
);
(new_block as *const BlockHeader).as_ref().unwrap()
};
let new_block_ptr = NonNull::new(new_block as *const _ as *mut _).unwrap();
let orig_next_block = suitable_block.next_block_as_mut();
orig_next_block.set_previous_header(new_block_ptr);
suitable_block.set_buffer_size(aligned_size);
let mapping_indices = calculate_mapping_indices(new_buffer_size);
tlsf_header.insert_block(new_block_ptr, mapping_indices);
}
suitable_block.set_freed(false);
tlsf_header.used_memory_size += suitable_block.buffer_size_with_header();
suitable_block.buffer_pointer_as::<u8>() as *mut u8
}
unsafe fn dealloc(&self, ptr: *mut u8, _layout: alloc::Layout) {
let block = {
(ptr.offset(-(BlockHeader::get_aligned_size() as isize)) as *mut BlockHeader)
.as_mut()
.unwrap()
};
block.set_freed(true);
let tlsf_header = self.tlsf_header();
tlsf_header.used_memory_size -= block.buffer_size_with_header();
{
let freed_block = block.buffer_pointer_as::<FreeNode>() as *mut FreeNode;
ptr::write(freed_block, FreeNode::new());
}
{
let next_block = block.next_block_as_mut();
if next_block.is_freed() {
let additonal_block_size = next_block.buffer_size_with_header();
tlsf_header
.extract_freed_block(NonNull::new(next_block as *mut BlockHeader).unwrap());
block.set_buffer_size(block.buffer_size() + additonal_block_size);
}
}
if block.is_prev_freed() {
let mut prev_block_ptr = block.previous_block_ptr().unwrap();
tlsf_header.extract_freed_block(prev_block_ptr);
let prev_block = prev_block_ptr.as_mut();
prev_block.set_buffer_size(prev_block.buffer_size() + block.buffer_size_with_header());
let mapping_indices = calculate_mapping_indices(prev_block.buffer_size());
tlsf_header.insert_block(prev_block_ptr, mapping_indices);
let prev_next_block = prev_block.next_block_as_mut();
prev_next_block.set_previous_freed(true);
prev_next_block.set_previous_header(prev_block_ptr);
} else {
let block_ptr = NonNull::new(block as *mut BlockHeader).unwrap();
tlsf_header.insert_block(block_ptr, calculate_mapping_indices(block.buffer_size()));
let next_block = block.next_block_as_mut();
next_block.set_previous_freed(true);
next_block.set_previous_header(block_ptr);
}
}
}
struct DynamicPool {
root_pool: RefCell<Option<RootPool>>,
additional_chunks: RefCell<ArrayVec<Option<TLSFChunk>, 32usize>>,
}
impl DynamicPool {
const fn new() -> Self {
Self {
root_pool: RefCell::new(None),
additional_chunks: RefCell::new(ArrayVec::<_, 32>::new_const()),
}
}
}
unsafe impl alloc::GlobalAlloc for DynamicPool {
unsafe fn alloc(&self, layout: alloc::Layout) -> *mut u8 {
if self.root_pool.borrow().is_none() {
self.root_pool.replace(Some(
RootPool::from(next_chunk_size(0, 0, layout.pad_to_align().size())).unwrap(),
));
}
let mut borrowed_root_pool = self.root_pool.borrow_mut();
let root_pool = borrowed_root_pool.as_mut().unwrap();
let mut new_pool_created = false;
let buffer: Option<*mut u8> = loop {
let buffer_ptr = root_pool.alloc(layout);
if buffer_ptr.is_null() == false {
break Some(buffer_ptr);
}
if new_pool_created {
break None;
}
let (len, capacity) = {
let borrow = self.additional_chunks.borrow();
(borrow.len(), borrow.capacity())
};
if len >= capacity {
break None;
}
let tlsf_header = root_pool.tlsf_header();
let last_chunk_size = {
if len == 0 {
tlsf_header.maximum_memory_size
} else {
let ref_chunks = self.additional_chunks.borrow();
ref_chunks[len - 1].as_ref().unwrap().layout.size()
}
};
let new_chunk_size = next_chunk_size(
tlsf_header.maximum_memory_size,
last_chunk_size,
calculate_allocation_size(layout.size()),
);
let new_chunk = match TLSFChunk::new(new_chunk_size) {
None => break None, Some(new_chunk) => new_chunk,
};
let mut chunk_list = self.additional_chunks.borrow_mut();
chunk_list.push(Some(new_chunk));
let back_index = chunk_list.len() - 1;
let used_chunk = tlsf_header.add_new_chunk(chunk_list[back_index].as_mut().unwrap());
root_pool.dealloc(used_chunk.unwrap().as_ptr(), layout);
new_pool_created = true;
};
match buffer {
None => null_mut(),
Some(ptr) => ptr,
}
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: alloc::Layout) {
assert!(ptr.is_null() == false, "");
assert!(self.root_pool.borrow().is_some(), "");
self.root_pool
.borrow()
.as_ref()
.unwrap()
.dealloc(ptr, layout);
}
}
pub struct TLSFAllocator {
pool: Mutex<DynamicPool>,
}
impl TLSFAllocator {
pub const fn new() -> Self {
Self {
pool: Mutex::new(DynamicPool::new()),
}
}
}
unsafe impl alloc::GlobalAlloc for TLSFAllocator {
unsafe fn alloc(&self, layout: alloc::Layout) -> *mut u8 {
self.pool.lock().alloc(layout)
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: alloc::Layout) {
self.pool.lock().dealloc(ptr, layout);
}
}
impl Drop for TLSFAllocator {
fn drop(&mut self) {}
}