1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
//! The module of base allocators.
//!
//! See [`BaseAlloc`] for more information.
#[cfg(feature = "base-mmap")]
mod mmap;
#[cfg(feature = "base-static")]
mod static_;
use core::{alloc::Layout, ptr::NonNull};
#[cfg(feature = "base-mmap")]
pub use self::mmap::Mmap;
#[cfg(feature = "base-static")]
pub use self::static_::Static;
use crate::arena::SLAB_SIZE;
/// A static memory handle, unable to be deallocated any longer.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct StaticHandle;
/// The trait of base allocators.
///
/// To support `no_std` features and various application scenarios, ferroc
/// serves itself and a middleware between the user and its base allocators.
///
/// Base allocators are generally allocating memory at a coarser granularity,
/// usually page-aligned.
///
/// The default implementations of `BaseAlloc` in this crate are [`Mmap`] backed
/// by [a Rust mmap interface](https://github.com/darfink/region-rs), and
/// [`Static`] backed by manual & static memory allocations.
///
/// # Safety
///
/// `allocate` must return a valid & free memory block containing `layout`,
/// zeroed if `IS_ZEROED`, if possible.
pub unsafe trait BaseAlloc: Sized {
/// Indicates if the base allocator are returning zeroed allocations by
/// default.
const IS_ZEROED: bool;
/// The opaque handle of this allocator, usually its metadata or for RAII
/// purposes.
type Handle;
/// The errors of the base allocator.
type Error: BaseError;
/// Allocate a memory [`Chunk`] of `layout`.
///
/// `commit` indicates whether the chunk should be committed right after the
/// allocation and don't need to be [`commit`](BaseAlloc::commit)ted again.
fn allocate(&self, layout: Layout, commit: bool) -> Result<Chunk<Self>, Self::Error>;
/// Deallocate a memory [`Chunk`].
///
/// Note that this function doesn't contain a receiver argument, since its
/// additional information should be contained in the
/// [`handle`](Chunk::handle) of the chunk.
///
/// # Safety
///
/// - `chunk` must point to a valid & owned memory block containing
/// `layout`, previously allocated by this allocator.
/// - `chunk` must not be used any longer after the deallocation.
unsafe fn deallocate(chunk: &mut Chunk<Self>);
/// Commit a block of memory in a memory chunk previously allocated by this
/// allocator.
///
/// # Safety
///
/// `ptr` must point to a block of memory in a memory chunk previously
/// allocated by this allocator.
unsafe fn commit(&self, ptr: NonNull<[u8]>) -> Result<(), Self::Error> {
let _ = ptr;
Ok(())
}
/// Decommit a block of memory in a memory chunk previously allocated by
/// this allocator.
///
/// # Errors
///
/// This function will return an error if the decommission failed.
///
/// # Safety
///
/// `ptr` must point to a block of memory whose content is no longer used.
unsafe fn decommit(&self, ptr: NonNull<[u8]>) {
let _ = ptr;
}
}
/// A marker trait depends on what features enabled:
///
/// - `error-log`: [`core::fmt::Display`];
pub trait BaseError {}
#[cfg(not(feature = "error-log"))]
impl<T> BaseError for T {}
#[cfg(feature = "error-log")]
impl<T> BaseError for T where T: core::fmt::Display {}
/// An owned representation of a valid memory block. Implementations like
/// `Clone` and `Copy` are banned for its unique ownership.
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct Chunk<B: BaseAlloc> {
ptr: NonNull<u8>,
layout: Layout,
pub handle: B::Handle,
}
unsafe impl<B: BaseAlloc> Send for Chunk<B> where B::Handle: Send {}
unsafe impl<B: BaseAlloc> Sync for Chunk<B> where B::Handle: Sync {}
impl<B: BaseAlloc> Chunk<B> {
/// Creates a memory chunk manually. This function should only be used by an
/// implementation of a base allocator.
///
/// # Panics
///
/// This function panics if the alignment of the layout is less then
/// [`SLAB_SIZE`].
///
/// # Safety
///
/// `ptr` must points to a valid & owned block of memory of `layout`, and
/// must be allocated from `base`.
pub const unsafe fn new(ptr: NonNull<u8>, layout: Layout, handle: B::Handle) -> Self {
assert!(
layout.align() <= SLAB_SIZE,
"the alignment must be greater than ferroc::arena::SLAB_SIZE",
);
Chunk { ptr, layout, handle }
}
/// Creates a static memory chunk.
///
/// # Safety
///
/// `ptr` must points to a valid, owned & static block of memory of
/// `layout`.
pub const unsafe fn from_static(ptr: NonNull<u8>, layout: Layout) -> Self
where
B: BaseAlloc<Handle = StaticHandle>,
{
Self::new(ptr, layout, StaticHandle)
}
/// Retrieves the layout information of this chunk.
pub fn layout(&self) -> Layout {
self.layout
}
/// Retrieves the pointer of this chunk.
pub fn pointer(&self) -> NonNull<[u8]> {
NonNull::from_raw_parts(self.ptr.cast(), self.layout.size())
}
}
impl<B: BaseAlloc> Drop for Chunk<B> {
fn drop(&mut self) {
// SAFETY: `chunk` points to a valid & owned memory block containing `layout`,
// previously allocated by this allocator.
unsafe { B::deallocate(self) }
}
}