Skip to main content

mimalloc_redirect/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(doc, deny(missing_docs))]
3
4use std::{
5    alloc::{GlobalAlloc, Layout},
6    ffi::{c_int, c_void},
7    fmt::{Display, Formatter, Result as FmtResult},
8};
9
10// Functions that are always needed for `MiMalloc`.
11unsafe extern "C" {
12    fn mi_version() -> c_int;
13
14    fn mi_free(ptr: *mut c_void);
15
16    fn mi_malloc_aligned(size: usize, alignment: usize) -> *mut c_void;
17
18    fn mi_zalloc_aligned(size: usize, alignment: usize) -> *mut c_void;
19
20    fn mi_realloc_aligned(ptr: *mut c_void, new_size: usize, alignment: usize) -> *mut c_void;
21}
22
23#[cfg(any(
24    all(
25        not(target_os = "windows"),
26        any(target_env = "gnu", target_env = "musl")
27    ),
28    target_os = "android",
29))]
30mod gnu_or_musl_wrapper {
31    use std::ffi::c_char;
32
33    use super::*;
34
35    unsafe extern "C" {
36        fn mi_malloc(size: usize) -> *mut c_void;
37
38        fn mi_calloc(count: usize, size: usize) -> *mut c_void;
39
40        fn mi_realloc(ptr: *mut c_void, new_size: usize) -> *mut c_void;
41
42        fn mi_strdup(s: *const c_char) -> *mut c_char;
43
44        fn mi_strndup(s: *const c_char, n: usize) -> *mut c_char;
45
46        fn mi_realpath(file_name: *const c_char, resolved_name: *mut c_char) -> *mut c_char;
47    }
48
49    #[unsafe(no_mangle)]
50    extern "C" fn __wrap_malloc(size: usize) -> *mut c_void {
51        unsafe { mi_malloc(size) }
52    }
53
54    #[unsafe(no_mangle)]
55    extern "C" fn __wrap_calloc(count: usize, size: usize) -> *mut c_void {
56        unsafe { mi_calloc(count, size) }
57    }
58
59    #[unsafe(no_mangle)]
60    extern "C" fn __wrap_realloc(ptr: *mut c_void, new_size: usize) -> *mut c_void {
61        unsafe { mi_realloc(ptr, new_size) }
62    }
63
64    #[unsafe(no_mangle)]
65    extern "C" fn __wrap_free(ptr: *mut c_void) {
66        unsafe { mi_free(ptr) }
67    }
68
69    #[unsafe(no_mangle)]
70    extern "C" fn __wrap_aligned_alloc(alignment: usize, size: usize) -> *mut c_void {
71        unsafe { mi_malloc_aligned(size, alignment) }
72    }
73
74    #[unsafe(no_mangle)]
75    extern "C" fn __wrap_strdup(s: *const c_char) -> *mut c_char {
76        unsafe { mi_strdup(s) }
77    }
78
79    #[unsafe(no_mangle)]
80    extern "C" fn __wrap_strndup(s: *const c_char, n: usize) -> *mut c_char {
81        unsafe { mi_strndup(s, n) }
82    }
83
84    #[unsafe(no_mangle)]
85    extern "C" fn __wrap_realpath(
86        file_name: *const c_char,
87        resolved_name: *mut c_char,
88    ) -> *mut c_char {
89        unsafe { mi_realpath(file_name, resolved_name) }
90    }
91
92    #[unsafe(no_mangle)]
93    extern "C" fn __wrap_posix_memalign(
94        out: *mut *mut c_void,
95        alignment: usize,
96        size: usize,
97    ) -> c_int {
98        if alignment < size_of::<usize>() || !alignment.is_power_of_two() {
99            return libc::EINVAL;
100        }
101
102        match unsafe { mi_malloc_aligned(size, alignment) } {
103            ptr if ptr.is_null() => libc::ENOMEM,
104            ptr => {
105                unsafe { out.write(ptr) }
106                0
107            }
108        }
109    }
110}
111
112#[cfg(target_os = "linux")]
113mod linux_wrapper {
114    use std::{ffi::c_void, ptr::null_mut};
115
116    use super::*;
117
118    unsafe extern "C" {
119        fn mi_usable_size(ptr: *mut c_void) -> usize;
120
121        fn mi_reallocf(ptr: *mut c_void, new_size: usize) -> *mut c_void;
122    }
123
124    #[unsafe(no_mangle)]
125    extern "C" fn __wrap_memalign(alignment: usize, size: usize) -> *mut c_void {
126        unsafe { mi_malloc_aligned(size, alignment) }
127    }
128
129    #[unsafe(no_mangle)]
130    extern "C" fn __wrap_valloc(size: usize) -> *mut c_void {
131        match usize::try_from(unsafe { libc::sysconf(libc::_SC_PAGESIZE) }) {
132            Ok(page_size) if page_size > 0 => unsafe { mi_malloc_aligned(size, page_size) },
133            _ => {
134                unsafe { libc::__errno_location().write(libc::EINVAL) }
135                null_mut()
136            }
137        }
138    }
139
140    #[unsafe(no_mangle)]
141    extern "C" fn __wrap_pvalloc(size: usize) -> *mut c_void {
142        match usize::try_from(unsafe { libc::sysconf(libc::_SC_PAGESIZE) }) {
143            Ok(page_size) if page_size > 0 => {
144                let alloc_size = size.div_ceil(page_size) * page_size;
145                unsafe { mi_malloc_aligned(alloc_size, page_size) }
146            }
147            _ => {
148                unsafe { libc::__errno_location().write(libc::EINVAL) }
149                null_mut()
150            }
151        }
152    }
153
154    #[unsafe(no_mangle)]
155    extern "C" fn __wrap_malloc_usable_size(ptr: *mut c_void) -> usize {
156        unsafe { mi_usable_size(ptr) }
157    }
158
159    #[unsafe(no_mangle)]
160    extern "C" fn __wrap_reallocf(ptr: *mut c_void, new_size: usize) -> *mut c_void {
161        unsafe { mi_reallocf(ptr, new_size) }
162    }
163}
164
165/// Version struct returned by [`MiMalloc::get_version`].
166#[derive(Debug, Copy, Clone, Default)]
167pub struct Version {
168    /// The major version.
169    pub major: u8,
170    /// The minor version.
171    pub minor: u8,
172    /// The patch version.
173    pub patch: u8,
174}
175
176impl Display for Version {
177    #[inline]
178    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
179        write!(f, "v{}.{}.{}", self.major, self.minor, self.patch)
180    }
181}
182
183/// Redirection to MiMalloc, usable with `#[global_allocator]` like so:
184/// ```
185/// use mimalloc_redirect::MiMalloc;
186///
187/// #[global_allocator]
188/// static ALLOCATOR: MiMalloc = MiMalloc;
189/// ```
190///
191/// See the [crate-level documentation](crate) for more information.
192#[derive(Debug, Copy, Clone, Default)]
193pub struct MiMalloc;
194impl MiMalloc {
195    /// Obtains the built-in MiMalloc version, which is currently `v3.2.8`.
196    #[inline]
197    pub fn get_version() -> Version {
198        // MiMalloc v3 calculates version as `1000 * major + 100 * minor + patch`.
199        let version = unsafe { mi_version() as i32 };
200        Version {
201            major: (version / 1000) as u8,
202            minor: ((version / 100) % 10) as u8,
203            patch: (version % 100) as u8,
204        }
205    }
206}
207
208unsafe impl GlobalAlloc for MiMalloc {
209    #[inline(always)]
210    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
211        unsafe { mi_malloc_aligned(layout.size(), layout.align()) as *mut u8 }
212    }
213
214    #[inline(always)]
215    unsafe fn dealloc(&self, ptr: *mut u8, _: Layout) {
216        unsafe { mi_free(ptr as *mut c_void) }
217    }
218
219    #[inline(always)]
220    unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
221        unsafe { mi_zalloc_aligned(layout.size(), layout.align()) as *mut u8 }
222    }
223
224    #[inline(always)]
225    unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
226        unsafe { mi_realloc_aligned(ptr as *mut c_void, new_size, layout.align()) as *mut u8 }
227    }
228}