baby_mimalloc/
mmap.rs

1use crate::Mimalloc;
2use core::alloc::{GlobalAlloc, Layout};
3use core::ffi::c_void;
4use core::ptr::null_mut;
5use libc::{mmap, munmap, sysconf};
6use libc::{MAP_ANONYMOUS, MAP_FAILED, MAP_PRIVATE, PROT_READ, PROT_WRITE, _SC_PAGE_SIZE};
7
8/// A simple `mmap`-based allocator that can be used to power [`Mimalloc`].
9///
10/// It is only used to allocate large chunks of memory and is not suitable for general malloc.
11#[derive(Default)]
12pub struct MmapAlloc;
13
14/// [`Mimalloc`] powered by `mmap` ([`MmapAlloc`]).
15pub type MimallocMmap = Mimalloc<MmapAlloc>;
16
17/// Create a new [`MimallocMmap`] instance by a `const fn`.
18pub const fn new_mimalloc_mmap() -> MimallocMmap {
19    Mimalloc::with_os_allocator(MmapAlloc)
20}
21
22unsafe fn mmap_anoymous(size: usize) -> *mut c_void {
23    mmap(
24        null_mut(),
25        size,
26        PROT_READ | PROT_WRITE,
27        MAP_PRIVATE | MAP_ANONYMOUS,
28        -1,
29        0,
30    )
31}
32
33unsafe impl GlobalAlloc for MmapAlloc {
34    /// See [`GlobalAlloc::alloc`].
35    ///
36    /// # Safety
37    ///
38    /// It can only allocate memory layouts whose size and alignment are multiples of the OS page size.
39    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
40        let size = layout.size();
41        let align = layout.align();
42
43        // `mmap` and `munmap` requires addresses to be aligned to page size
44        debug_assert!(size % sysconf(_SC_PAGE_SIZE) as usize == 0);
45        debug_assert!(align % sysconf(_SC_PAGE_SIZE) as usize == 0);
46
47        // try mapping exactly `size` at first
48        let p = mmap_anoymous(size);
49        if p == MAP_FAILED {
50            return null_mut();
51        }
52
53        if p as usize % align == 0 {
54            // aligned
55            return p.cast();
56        }
57        // not aligned
58        munmap(p, size);
59
60        // over allocate to ensure alignment
61        let start = mmap_anoymous(size + align - 1);
62        if start == MAP_FAILED {
63            return null_mut();
64        }
65
66        let offset = start.align_offset(align);
67        let aligned = start.add(offset);
68        if offset != 0 {
69            munmap(start, offset);
70        }
71        if offset != align - 1 {
72            let end = aligned.add(size);
73            munmap(end, align - 1 - offset);
74        }
75        aligned.cast()
76    }
77
78    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
79        munmap(ptr.cast(), layout.size());
80    }
81}