#![doc = include_str!("../README.md")]
#![cfg_attr(not(any(feature = "std", test)), no_std)]
use core::{
alloc::Layout,
cell::UnsafeCell,
ptr::{NonNull, copy_nonoverlapping},
};
use ecore::int::PrimaryUInt;
pub use self::{
block::{ElementCount, simple::SimpleFirstFit},
dv::DesignatedVictim,
first_fit::FirstFit,
half_tree::HalfTree,
ptr::{FixedBase, FixedBasePtr},
tlsf::{Tlsf, TlsfParms},
};
mod block;
mod dv;
mod first_fit;
mod half_tree;
mod ptr;
mod tlsf;
pub struct AllocError;
pub unsafe trait Allocator {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout);
fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
let mut block = self.allocate(layout)?;
unsafe { block.as_mut().fill(0) };
Ok(block)
}
unsafe fn grow(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
debug_assert!(new_layout.size() >= old_layout.size(), "`new_layout.size()` must be greater than or equal to `old_layout.size()`");
let new_ptr = self.allocate(new_layout)?;
unsafe { copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr().cast(), old_layout.size()) }
unsafe { self.deallocate(ptr, old_layout) }
Ok(new_ptr)
}
unsafe fn grow_zeroed(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
debug_assert!(new_layout.size() >= old_layout.size(), "`new_layout.size()` must be greater than or equal to `old_layout.size()`");
let new_ptr = self.allocate_zeroed(new_layout)?;
unsafe { copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr().cast(), old_layout.size()) }
unsafe { self.deallocate(ptr, old_layout) }
Ok(new_ptr)
}
unsafe fn shrink(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
debug_assert!(new_layout.size() <= old_layout.size(), "`new_layout.size()` must be smaller than or equal to `old_layout.size()`");
let new_ptr = self.allocate(new_layout)?;
unsafe { copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr().cast(), new_layout.size()) }
unsafe { self.deallocate(ptr, old_layout) }
Ok(new_ptr)
}
fn by_ref(&self) -> &Self
where
Self: Sized,
{
self
}
}
pub trait FreeBlockManager {
type StateElement: PrimaryUInt;
type Element;
type Node;
const MAX_ELEMENT_COUNT: ElementCount<Self::StateElement, Self::Element>;
fn address_range(&self) -> (usize, usize);
fn take_out(&mut self, element_count: ElementCount<Self::StateElement, Self::Element>) -> Option<NonNull<Self::Node>>;
unsafe fn register(&mut self, node: NonNull<Self::Node>);
unsafe fn unregister(&mut self, node: NonNull<Self::Node>);
unsafe fn init(&mut self, ptr: NonNull<Self::Element>) {
unsafe { self.register(ptr.cast()) };
}
unsafe fn extend(&mut self, ptr: NonNull<Self::Element>) {
unsafe { self.register(ptr.cast()) };
}
fn after_allocate(&mut self, ptr: NonNull<u8>, layout: Layout) {
let _ = (ptr, layout);
}
fn before_deallocate(&mut self, ptr: NonNull<u8>, layout: Layout) {
let _ = (ptr, layout);
}
#[cfg(feature = "test-utils")]
fn validate(&mut self, addr: core::num::NonZero<usize>) {
let _ = addr;
}
}
pub trait AllocatorMutex {
type Guard<'g>
where
Self: 'g;
#[must_use = "unlock when drop"]
fn lock(&self) -> Self::Guard<'_>;
}
pub struct LeanFlexAllocator<Mutex, Manager: ?Sized + FreeBlockManager> {
mutex: Mutex,
raw: UnsafeCell<block::RawAllocator<Manager>>,
}
unsafe impl<Mutex: Sync, Manager: ?Sized + FreeBlockManager> Sync for LeanFlexAllocator<Mutex, Manager> {}
impl<Mutex, Manager: FreeBlockManager> LeanFlexAllocator<Mutex, Manager> {
pub const fn new(mutex: Mutex, manager: Manager) -> Self {
Self { mutex, raw: UnsafeCell::new(block::RawAllocator::new(manager)) }
}
}
impl<Manager: FreeBlockManager> LeanFlexAllocator<NoopMutex, Manager> {
pub const fn new_without_mutex(manager: Manager) -> Self {
Self { mutex: NoopMutex::new(), raw: UnsafeCell::new(block::RawAllocator::new(manager)) }
}
}
pub struct NoopMutex(UnsafeCell<()>);
impl Default for NoopMutex {
fn default() -> Self {
Self::new()
}
}
impl NoopMutex {
pub const fn new() -> Self {
Self(UnsafeCell::new(()))
}
}
impl AllocatorMutex for NoopMutex {
type Guard<'g>
= ()
where
Self: 'g;
fn lock(&self) -> Self::Guard<'_> {}
}
#[cfg(any(feature = "std", test))]
impl<T: ?Sized> AllocatorMutex for std::sync::Mutex<T> {
type Guard<'g>
= std::sync::MutexGuard<'g, T>
where
Self: 'g;
fn lock(&self) -> Self::Guard<'_> {
Self::lock(self).unwrap()
}
}