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