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(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#[derive(Debug, Copy, Clone, Default)]
171pub struct Version {
172 pub major: u8,
174 pub minor: u8,
176 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#[derive(Debug, Copy, Clone, Default)]
197pub struct MiMalloc;
198impl MiMalloc {
199 #[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}