1use crate::MiMalloc;
4use core::ffi::c_void;
5use core::ptr::NonNull;
6
7#[inline]
14pub fn set_current_thread_in_threadpool() {
15 unsafe { rustfs_mimalloc_sys::mi_thread_set_in_threadpool() }
16}
17
18#[derive(Debug, Clone, Copy, Default)]
20pub struct ProcessInfo {
21 pub elapsed_msecs: usize,
22 pub user_msecs: usize,
23 pub system_msecs: usize,
24 pub current_rss: usize,
25 pub peak_rss: usize,
26 pub current_commit: usize,
27 pub peak_commit: usize,
28 pub page_faults: usize,
29}
30
31impl MiMalloc {
32 #[inline]
34 pub fn version() -> i32 {
35 unsafe { rustfs_mimalloc_sys::mi_version() }
36 }
37
38 #[inline]
40 pub fn collect(force: bool) {
41 unsafe { rustfs_mimalloc_sys::mi_collect(force) }
42 }
43
44 #[inline]
49 pub unsafe fn usable_size(ptr: *const u8) -> usize {
50 unsafe { rustfs_mimalloc_sys::mi_usable_size(ptr as *const c_void) }
51 }
52
53 #[inline]
58 pub const fn wsize_from_size(size: usize) -> usize {
59 rustfs_mimalloc_sys::mi_wsize_from_size(size)
60 }
61
62 #[inline]
70 pub unsafe fn malloc_csize(size: usize) -> *mut u8 {
71 unsafe { rustfs_mimalloc_sys::mi_malloc_csize(size) as *mut u8 }
72 }
73
74 #[inline]
82 pub unsafe fn zalloc_csize(size: usize) -> *mut u8 {
83 unsafe { rustfs_mimalloc_sys::mi_zalloc_csize(size) as *mut u8 }
84 }
85
86 #[inline]
93 pub unsafe fn wmalloc_small(wsize: usize) -> *mut u8 {
94 unsafe { rustfs_mimalloc_sys::mi_wmalloc_small(wsize) as *mut u8 }
95 }
96
97 #[inline]
104 pub unsafe fn wzalloc_small(wsize: usize) -> *mut u8 {
105 unsafe { rustfs_mimalloc_sys::mi_wzalloc_small(wsize) as *mut u8 }
106 }
107
108 #[inline]
116 pub unsafe fn free_csize(ptr: *mut u8, size: usize) {
117 unsafe { rustfs_mimalloc_sys::mi_free_csize(ptr as *mut c_void, size) }
118 }
119
120 #[inline]
128 pub unsafe fn free_csize_nonnull(ptr: NonNull<u8>, size: usize) {
129 unsafe { rustfs_mimalloc_sys::mi_free_csize_nonnull(ptr.as_ptr() as *mut c_void, size) }
130 }
131
132 #[inline]
138 pub unsafe fn free_small(ptr: *mut u8) {
139 unsafe { rustfs_mimalloc_sys::mi_free_small(ptr as *mut c_void) }
140 }
141
142 #[inline]
148 pub unsafe fn free_small_nonnull(ptr: NonNull<u8>) {
149 unsafe { rustfs_mimalloc_sys::mi_free_small_nonnull(ptr.as_ptr() as *mut c_void) }
150 }
151
152 pub fn process_info() -> ProcessInfo {
154 let mut info = ProcessInfo::default();
155 unsafe {
156 rustfs_mimalloc_sys::mi_process_info(
157 &mut info.elapsed_msecs,
158 &mut info.user_msecs,
159 &mut info.system_msecs,
160 &mut info.current_rss,
161 &mut info.peak_rss,
162 &mut info.current_commit,
163 &mut info.peak_commit,
164 &mut info.page_faults,
165 );
166 }
167 info
168 }
169
170 pub fn stats_json() -> String {
174 unsafe {
175 crate::ffi::owned_mimalloc_string(rustfs_mimalloc_sys::mi_stats_get_json(
176 0,
177 core::ptr::null_mut(),
178 ))
179 }
180 }
181
182 pub fn stats_print() -> String {
184 crate::ffi::collect_mimalloc_output(|out, arg| unsafe {
185 rustfs_mimalloc_sys::mi_stats_print_out(out, arg);
186 })
187 }
188
189 #[inline]
191 pub fn stats_reset() {
192 unsafe { rustfs_mimalloc_sys::mi_stats_reset() }
193 }
194
195 pub fn process_info_print() -> String {
197 crate::ffi::collect_mimalloc_output(|out, arg| unsafe {
198 rustfs_mimalloc_sys::mi_process_info_print_out(out, arg);
199 })
200 }
201
202 #[inline]
206 pub fn option_is_enabled(option: rustfs_mimalloc_sys::mi_option_t) -> bool {
207 unsafe { rustfs_mimalloc_sys::mi_option_is_enabled(option) }
208 }
209
210 #[inline]
212 pub fn option_get(option: rustfs_mimalloc_sys::mi_option_t) -> rustfs_mimalloc_sys::c_long {
213 unsafe { rustfs_mimalloc_sys::mi_option_get(option) }
214 }
215
216 #[inline]
218 pub fn option_get_size(option: rustfs_mimalloc_sys::mi_option_t) -> usize {
219 unsafe { rustfs_mimalloc_sys::mi_option_get_size(option) }
220 }
221
222 #[inline]
232 pub fn option_set(
233 option: rustfs_mimalloc_sys::mi_option_t,
234 value: rustfs_mimalloc_sys::c_long,
235 ) {
236 unsafe { rustfs_mimalloc_sys::mi_option_set(option, value) }
237 }
238
239 #[inline]
241 pub fn option_enable(option: rustfs_mimalloc_sys::mi_option_t) {
242 unsafe { rustfs_mimalloc_sys::mi_option_enable(option) }
243 }
244
245 #[inline]
247 pub fn option_disable(option: rustfs_mimalloc_sys::mi_option_t) {
248 unsafe { rustfs_mimalloc_sys::mi_option_disable(option) }
249 }
250}
251
252#[cfg(test)]
255mod tests {
256 use super::*;
257 use rustfs_mimalloc_sys::mi_option_t;
258
259 #[test]
260 fn version_is_v3() {
261 assert!(MiMalloc::version() >= 30502, "expected >= V3.5.2");
262 }
263
264 #[test]
265 fn stats_json_not_empty() {
266 let json = MiMalloc::stats_json();
267 assert!(!json.is_empty());
268 }
269
270 #[test]
271 fn stats_print_not_empty() {
272 let stats = MiMalloc::stats_print();
273 assert!(!stats.is_empty());
274 }
275
276 #[test]
277 fn stats_reset_smoke() {
278 MiMalloc::stats_reset();
279 }
280
281 #[test]
282 fn process_info_print_not_empty() {
283 let info = MiMalloc::process_info_print();
284 assert!(!info.is_empty());
285 }
286
287 #[test]
288 fn option_roundtrip() {
289 let _ = MiMalloc::option_get(mi_option_t::mi_option_purge_delay);
291 let _ = MiMalloc::option_is_enabled(mi_option_t::mi_option_show_errors);
292 }
293
294 #[test]
295 fn process_info_smoke() {
296 let info = MiMalloc::process_info();
297 let _ = info;
298 }
299
300 #[test]
301 fn set_current_thread_in_threadpool_smoke() {
302 set_current_thread_in_threadpool();
303 set_current_thread_in_threadpool();
304 }
305
306 #[test]
307 fn usable_size_at_least_requested() {
308 unsafe {
309 let ptr = rustfs_mimalloc_sys::mi_malloc(64);
310 assert!(MiMalloc::usable_size(ptr as *const u8) >= 64);
311 rustfs_mimalloc_sys::mi_free(ptr);
312 }
313 }
314
315 #[test]
316 fn free_small_nonnull_smoke() {
317 unsafe {
318 let ptr = rustfs_mimalloc_sys::mi_malloc_small(64);
319 let ptr = NonNull::new(ptr as *mut u8).expect("mi_malloc_small returned null");
320 MiMalloc::free_small_nonnull(ptr);
321 }
322 }
323
324 #[test]
325 fn word_size_small_alloc_smoke() {
326 unsafe {
327 let wsize = MiMalloc::wsize_from_size(64);
328
329 let ptr =
330 NonNull::new(MiMalloc::wmalloc_small(wsize)).expect("wmalloc_small returned null");
331 MiMalloc::free_small_nonnull(ptr);
332
333 let zeroed =
334 NonNull::new(MiMalloc::wzalloc_small(wsize)).expect("wzalloc_small returned null");
335 assert!((0..64).all(|i| *zeroed.as_ptr().add(i) == 0));
336 MiMalloc::free_small_nonnull(zeroed);
337 }
338 }
339
340 #[test]
341 fn csize_alloc_smoke() {
342 unsafe {
343 let ptr = NonNull::new(MiMalloc::malloc_csize(64)).expect("malloc_csize returned null");
344 MiMalloc::free_csize_nonnull(ptr, 64);
345
346 let zeroed =
347 NonNull::new(MiMalloc::zalloc_csize(64)).expect("zalloc_csize returned null");
348 assert!((0..64).all(|i| *zeroed.as_ptr().add(i) == 0));
349 MiMalloc::free_csize_nonnull(zeroed, 64);
350 }
351 }
352
353 #[test]
354 fn sys_theap_csize_alloc_smoke() {
355 unsafe {
356 let heap = rustfs_mimalloc_sys::mi_heap_new();
357 assert!(!heap.is_null(), "mi_heap_new returned null");
358
359 let theap = rustfs_mimalloc_sys::mi_heap_theap(heap);
360 assert!(!theap.is_null(), "mi_heap_theap returned null");
361
362 let ptr =
363 NonNull::new(rustfs_mimalloc_sys::mi_theap_malloc_csize(theap, 64) as *mut u8)
364 .expect("mi_theap_malloc_csize returned null");
365 MiMalloc::free_csize_nonnull(ptr, 64);
366
367 let zeroed =
368 NonNull::new(rustfs_mimalloc_sys::mi_theap_zalloc_csize(theap, 64) as *mut u8)
369 .expect("mi_theap_zalloc_csize returned null");
370 assert!((0..64).all(|i| *zeroed.as_ptr().add(i) == 0));
371 MiMalloc::free_csize_nonnull(zeroed, 64);
372
373 rustfs_mimalloc_sys::mi_heap_delete(heap);
374 }
375 }
376
377 #[test]
378 fn free_csize_routes_small_and_large() {
379 unsafe {
380 let small = rustfs_mimalloc_sys::mi_malloc_small(64);
381 MiMalloc::free_csize(small as *mut u8, 64);
382
383 let large_size = rustfs_mimalloc_sys::MI_SMALL_SIZE_MAX + 64;
384 let large = rustfs_mimalloc_sys::mi_malloc(large_size);
385 let large = NonNull::new(large as *mut u8).expect("mi_malloc returned null");
386 MiMalloc::free_csize_nonnull(large, large_size);
387 }
388 }
389}