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
10unsafe 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#[derive(Debug, Copy, Clone, Default)]
167pub struct Version {
168 pub major: u8,
170 pub minor: u8,
172 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#[derive(Debug, Copy, Clone, Default)]
193pub struct MiMalloc;
194impl MiMalloc {
195 #[inline]
197 pub fn get_version() -> Version {
198 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}