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]
61 pub unsafe fn free_csize(ptr: *mut u8, size: usize) {
62 unsafe { rustfs_mimalloc_sys::mi_free_csize(ptr as *mut c_void, size) }
63 }
64
65 #[inline]
73 pub unsafe fn free_csize_nonnull(ptr: NonNull<u8>, size: usize) {
74 unsafe { rustfs_mimalloc_sys::mi_free_csize_nonnull(ptr.as_ptr() as *mut c_void, size) }
75 }
76
77 #[inline]
83 pub unsafe fn free_small(ptr: *mut u8) {
84 unsafe { rustfs_mimalloc_sys::mi_free_small(ptr as *mut c_void) }
85 }
86
87 #[inline]
93 pub unsafe fn free_small_nonnull(ptr: NonNull<u8>) {
94 unsafe { rustfs_mimalloc_sys::mi_free_small_nonnull(ptr.as_ptr() as *mut c_void) }
95 }
96
97 pub fn process_info() -> ProcessInfo {
99 let mut info = ProcessInfo::default();
100 unsafe {
101 rustfs_mimalloc_sys::mi_process_info(
102 &mut info.elapsed_msecs,
103 &mut info.user_msecs,
104 &mut info.system_msecs,
105 &mut info.current_rss,
106 &mut info.peak_rss,
107 &mut info.current_commit,
108 &mut info.peak_commit,
109 &mut info.page_faults,
110 );
111 }
112 info
113 }
114
115 pub fn stats_json() -> String {
119 unsafe {
120 crate::ffi::owned_mimalloc_string(rustfs_mimalloc_sys::mi_stats_get_json(
121 0,
122 core::ptr::null_mut(),
123 ))
124 }
125 }
126
127 pub fn stats_print() -> String {
129 crate::ffi::collect_mimalloc_output(|out, arg| unsafe {
130 rustfs_mimalloc_sys::mi_stats_print_out(out, arg);
131 })
132 }
133
134 #[inline]
136 pub fn stats_reset() {
137 unsafe { rustfs_mimalloc_sys::mi_stats_reset() }
138 }
139
140 pub fn process_info_print() -> String {
142 crate::ffi::collect_mimalloc_output(|out, arg| unsafe {
143 rustfs_mimalloc_sys::mi_process_info_print_out(out, arg);
144 })
145 }
146
147 #[inline]
151 pub fn option_is_enabled(option: rustfs_mimalloc_sys::mi_option_t) -> bool {
152 unsafe { rustfs_mimalloc_sys::mi_option_is_enabled(option) }
153 }
154
155 #[inline]
157 pub fn option_get(option: rustfs_mimalloc_sys::mi_option_t) -> rustfs_mimalloc_sys::c_long {
158 unsafe { rustfs_mimalloc_sys::mi_option_get(option) }
159 }
160
161 #[inline]
163 pub fn option_get_size(option: rustfs_mimalloc_sys::mi_option_t) -> usize {
164 unsafe { rustfs_mimalloc_sys::mi_option_get_size(option) }
165 }
166
167 #[inline]
177 pub fn option_set(
178 option: rustfs_mimalloc_sys::mi_option_t,
179 value: rustfs_mimalloc_sys::c_long,
180 ) {
181 unsafe { rustfs_mimalloc_sys::mi_option_set(option, value) }
182 }
183
184 #[inline]
186 pub fn option_enable(option: rustfs_mimalloc_sys::mi_option_t) {
187 unsafe { rustfs_mimalloc_sys::mi_option_enable(option) }
188 }
189
190 #[inline]
192 pub fn option_disable(option: rustfs_mimalloc_sys::mi_option_t) {
193 unsafe { rustfs_mimalloc_sys::mi_option_disable(option) }
194 }
195}
196
197#[cfg(test)]
200mod tests {
201 use super::*;
202 use rustfs_mimalloc_sys::mi_option_t;
203
204 #[test]
205 fn version_is_v3() {
206 assert!(MiMalloc::version() >= 30501, "expected >= V3.5.1");
207 }
208
209 #[test]
210 fn stats_json_not_empty() {
211 let json = MiMalloc::stats_json();
212 assert!(!json.is_empty());
213 }
214
215 #[test]
216 fn stats_print_not_empty() {
217 let stats = MiMalloc::stats_print();
218 assert!(!stats.is_empty());
219 }
220
221 #[test]
222 fn stats_reset_smoke() {
223 MiMalloc::stats_reset();
224 }
225
226 #[test]
227 fn process_info_print_not_empty() {
228 let info = MiMalloc::process_info_print();
229 assert!(!info.is_empty());
230 }
231
232 #[test]
233 fn option_roundtrip() {
234 let _ = MiMalloc::option_get(mi_option_t::mi_option_purge_delay);
236 let _ = MiMalloc::option_is_enabled(mi_option_t::mi_option_show_errors);
237 }
238
239 #[test]
240 fn process_info_smoke() {
241 let info = MiMalloc::process_info();
242 let _ = info;
243 }
244
245 #[test]
246 fn set_current_thread_in_threadpool_smoke() {
247 set_current_thread_in_threadpool();
248 set_current_thread_in_threadpool();
249 }
250
251 #[test]
252 fn usable_size_at_least_requested() {
253 unsafe {
254 let ptr = rustfs_mimalloc_sys::mi_malloc(64);
255 assert!(MiMalloc::usable_size(ptr as *const u8) >= 64);
256 rustfs_mimalloc_sys::mi_free(ptr);
257 }
258 }
259
260 #[test]
261 fn free_small_nonnull_smoke() {
262 unsafe {
263 let ptr = rustfs_mimalloc_sys::mi_malloc_small(64);
264 let ptr = NonNull::new(ptr as *mut u8).expect("mi_malloc_small returned null");
265 MiMalloc::free_small_nonnull(ptr);
266 }
267 }
268
269 #[test]
270 fn free_csize_routes_small_and_large() {
271 unsafe {
272 let small = rustfs_mimalloc_sys::mi_malloc_small(64);
273 MiMalloc::free_csize(small as *mut u8, 64);
274
275 let large_size = rustfs_mimalloc_sys::MI_SMALL_SIZE_MAX + 64;
276 let large = rustfs_mimalloc_sys::mi_malloc(large_size);
277 let large = NonNull::new(large as *mut u8).expect("mi_malloc returned null");
278 MiMalloc::free_csize_nonnull(large, large_size);
279 }
280 }
281}