Skip to main content

rustfs_mimalloc_sys/
lib.rs

1//! Low-level FFI bindings to [mimalloc](https://github.com/microsoft/mimalloc) V3 (v3.5.2).
2//!
3//! For a safe wrapper, use the `rustfs-mimalloc` crate.
4
5#![no_std]
6#![allow(non_camel_case_types)]
7
8// ── Type aliases ────────────────────────────────────────────────────────────
9
10pub use core::ffi::{c_char, c_int, c_long, c_void};
11
12pub type size_t = usize;
13
14// ── Constants ──────────────────────────────────────────────────────────────
15
16/// Maximum word count for mimalloc's small allocation fast path.
17pub const MI_SMALL_WSIZE_MAX: size_t = 128;
18
19/// Maximum byte size for mimalloc's small allocation fast path.
20pub const MI_SMALL_SIZE_MAX: size_t = MI_SMALL_WSIZE_MAX * core::mem::size_of::<size_t>();
21
22/// Maximum user data bytes stored inline with a sampled profiling allocation.
23pub const MI_PROFILE_SAMPLE_DATA_MAX_SIZE: size_t = 1024;
24
25// ── Opaque types ────────────────────────────────────────────────────────────
26
27#[repr(C)]
28pub struct mi_heap_t {
29    _opaque: [u8; 0],
30}
31
32#[repr(C)]
33pub struct mi_theap_t {
34    _opaque: [u8; 0],
35}
36
37/// Subprocess identifier. Opaque handle — do not access fields directly.
38#[repr(C)]
39#[derive(Clone, Copy)]
40pub struct mi_subproc_id_t {
41    _id: *mut c_void,
42}
43
44/// Arena identifier. Opaque handle.
45pub type mi_arena_id_t = *mut c_void;
46
47// ── Option enum ─────────────────────────────────────────────────────────────
48//
49// Kept in sync with mimalloc V3.5.2 `mi_option_e` in `mimalloc.h`.
50
51#[repr(C)]
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub enum mi_option_t {
54    mi_option_show_errors = 0,
55    mi_option_show_stats = 1,
56    mi_option_verbose = 2,
57    mi_option_arena_eager_commit = 4,
58    mi_option_purge_decommits = 5,
59    mi_option_allow_large_os_pages = 6,
60    mi_option_reserve_huge_os_pages = 7,
61    mi_option_reserve_huge_os_pages_at = 8,
62    mi_option_reserve_os_memory = 9,
63    mi_option_purge_delay = 15,
64    mi_option_use_numa_nodes = 16,
65    mi_option_disallow_os_alloc = 17,
66    mi_option_os_tag = 18,
67    mi_option_max_errors = 19,
68    mi_option_max_warnings = 20,
69    mi_option_destroy_on_exit = 22,
70    mi_option_arena_reserve = 23,
71    mi_option_arena_purge_mult = 24,
72    mi_option_disallow_arena_alloc = 26,
73    mi_option_retry_on_oom = 27,
74    mi_option_guarded_min = 29,
75    mi_option_guarded_max = 30,
76    mi_option_guarded_precise = 31,
77    mi_option_guarded_sample_rate = 32,
78    mi_option_guarded_sample_seed = 33,
79    mi_option_generic_collect = 34,
80    mi_option_page_reclaim_on_free = 35,
81    mi_option_page_full_retain = 36,
82    mi_option_page_max_candidates = 37,
83    mi_option_max_vabits = 38,
84    mi_option_pagemap_commit = 39,
85    mi_option_page_commit_on_demand = 40,
86    mi_option_page_max_reclaim = 41,
87    mi_option_page_cross_thread_max_reclaim = 42,
88    mi_option_allow_thp = 43,
89    mi_option_minimal_purge_size = 44,
90    mi_option_arena_max_object_size = 45,
91    mi_option_arena_is_numa_local = 46,
92    mi_option_collect_merges_stats = 47,
93}
94
95// ── Heap area (for visiting blocks) ─────────────────────────────────────────
96
97#[repr(C)]
98pub struct mi_heap_area_t {
99    pub blocks: *mut c_void,
100    pub reserved: size_t,
101    pub committed: size_t,
102    pub used: size_t,
103    pub block_size: size_t,
104    pub full_block_size: size_t,
105    pub reserved1: *mut c_void,
106}
107
108// ── Callback types ──────────────────────────────────────────────────────────
109
110pub type mi_output_fun = unsafe extern "C" fn(msg: *const c_char, arg: *mut c_void);
111pub type mi_error_fun = unsafe extern "C" fn(err: c_int, arg: *mut c_void);
112pub type mi_deferred_free_fun = unsafe extern "C" fn(force: bool, heartbeat: u64, arg: *mut c_void);
113pub type mi_profiler_on_alloc_fun = unsafe extern "C" fn(
114    profiler: *mut mi_profiler_t,
115    profiler_data: *mut mi_profiler_sample_data_t,
116    ptr: *mut c_void,
117    requested_size: size_t,
118    bytes_sample_rate: size_t,
119    bytes_since_last_sample: u64,
120    heap: *const mi_heap_t,
121) -> size_t;
122pub type mi_profiler_on_realloc_inplace_fun = unsafe extern "C" fn(
123    profiler: *mut mi_profiler_t,
124    profiler_data: *mut mi_profiler_sample_data_t,
125    ptr: *mut c_void,
126    old_size: size_t,
127    heap: *const mi_heap_t,
128) -> size_t;
129pub type mi_profiler_on_free_fun = unsafe extern "C" fn(
130    profiler: *mut mi_profiler_t,
131    profiler_data: *mut mi_profiler_sample_data_t,
132    ptr: *mut c_void,
133    heap: *const mi_heap_t,
134);
135pub type mi_block_visit_fun = unsafe extern "C" fn(
136    heap: *const mi_heap_t,
137    area: *const mi_heap_area_t,
138    block: *mut c_void,
139    block_size: size_t,
140    arg: *mut c_void,
141) -> bool;
142pub type mi_heap_visit_fun = unsafe extern "C" fn(heap: *mut mi_heap_t, arg: *mut c_void) -> bool;
143
144// ── Profiling ───────────────────────────────────────────────────────────────
145
146#[repr(C)]
147pub struct mi_profiler_sample_data_t {
148    pub user_data_size: size_t,
149    pub user_data: [*mut c_void; 1],
150}
151
152/// Experimental mimalloc profiling hook table.
153#[repr(C)]
154pub struct mi_profiler_t {
155    pub reserved: *mut c_void,
156    pub sample_data_size: size_t,
157    pub initial_sample_rate: size_t,
158    pub on_alloc: Option<mi_profiler_on_alloc_fun>,
159    pub on_free: Option<mi_profiler_on_free_fun>,
160    pub on_realloc_inplace: Option<mi_profiler_on_realloc_inplace_fun>,
161}
162
163// ── Standard malloc interface ───────────────────────────────────────────────
164
165unsafe extern "C" {
166    pub fn mi_malloc(size: size_t) -> *mut c_void;
167    pub fn mi_calloc(count: size_t, size: size_t) -> *mut c_void;
168    pub fn mi_realloc(p: *mut c_void, newsize: size_t) -> *mut c_void;
169    pub fn mi_free(p: *mut c_void);
170    pub fn mi_strdup(s: *const c_char) -> *mut c_char;
171}
172
173// ── Extended allocation ─────────────────────────────────────────────────────
174
175unsafe extern "C" {
176    pub fn mi_malloc_small(size: size_t) -> *mut c_void;
177    pub fn mi_zalloc_small(size: size_t) -> *mut c_void;
178    pub fn mi_wmalloc_small(wsize: size_t) -> *mut c_void;
179    pub fn mi_wzalloc_small(wsize: size_t) -> *mut c_void;
180    pub fn mi_zalloc(size: size_t) -> *mut c_void;
181    pub fn mi_mallocn(count: size_t, size: size_t) -> *mut c_void;
182    pub fn mi_reallocn(p: *mut c_void, count: size_t, size: size_t) -> *mut c_void;
183    pub fn mi_usable_size(p: *const c_void) -> size_t;
184    pub fn mi_good_size(size: size_t) -> size_t;
185    pub fn mi_free_size(p: *mut c_void, size: size_t);
186    pub fn mi_free_small(p: *mut c_void);
187    pub fn mi_free_small_nonnull(p: *mut c_void);
188}
189
190/// Convert a byte size to a mimalloc machine-word count.
191#[inline]
192pub const fn mi_wsize_from_size(size: size_t) -> size_t {
193    size.div_ceil(core::mem::size_of::<size_t>())
194}
195
196/// Allocate when the size is statically known by the caller.
197///
198/// This mirrors mimalloc's inline `mi_malloc_csize` helper.
199///
200/// # Safety
201/// The returned pointer must be checked for null and freed with a compatible
202/// mimalloc free API. The caller is responsible for honoring raw allocation
203/// pointer aliasing and lifetime rules.
204#[inline]
205pub unsafe fn mi_malloc_csize(size: size_t) -> *mut c_void {
206    if size <= MI_SMALL_SIZE_MAX {
207        unsafe { mi_wmalloc_small(mi_wsize_from_size(size)) }
208    } else {
209        unsafe { mi_malloc(size) }
210    }
211}
212
213/// Allocate zeroed memory when the size is statically known by the caller.
214///
215/// This mirrors mimalloc's inline `mi_zalloc_csize` helper.
216///
217/// # Safety
218/// The returned pointer must be checked for null and freed with a compatible
219/// mimalloc free API. The caller is responsible for honoring raw allocation
220/// pointer aliasing and lifetime rules.
221#[inline]
222pub unsafe fn mi_zalloc_csize(size: size_t) -> *mut c_void {
223    if size <= MI_SMALL_SIZE_MAX {
224        unsafe { mi_wzalloc_small(mi_wsize_from_size(size)) }
225    } else {
226        unsafe { mi_zalloc(size) }
227    }
228}
229
230/// Free an allocation when the size is statically known by the caller.
231///
232/// This mirrors mimalloc's inline `mi_free_csize` helper.
233///
234/// # Safety
235///
236/// `p` must be null or a valid mimalloc allocation, and `size` must be the
237/// allocation size used for the corresponding allocation.
238#[inline]
239pub unsafe fn mi_free_csize(p: *mut c_void, size: size_t) {
240    if size <= MI_SMALL_SIZE_MAX {
241        unsafe { mi_free_small(p) };
242    } else {
243        unsafe { mi_free(p) };
244    }
245}
246
247/// Free a non-null allocation when the size is statically known by the caller.
248///
249/// This mirrors mimalloc's inline `mi_free_csize_nonnull` helper.
250///
251/// # Safety
252///
253/// `p` must be a non-null valid mimalloc allocation, and `size` must be the
254/// allocation size used for the corresponding allocation.
255#[inline]
256pub unsafe fn mi_free_csize_nonnull(p: *mut c_void, size: size_t) {
257    if size <= MI_SMALL_SIZE_MAX {
258        unsafe { mi_free_small_nonnull(p) };
259    } else {
260        unsafe { mi_free(p) };
261    }
262}
263
264// ── Aligned allocation ──────────────────────────────────────────────────────
265
266unsafe extern "C" {
267    pub fn mi_malloc_aligned(size: size_t, alignment: size_t) -> *mut c_void;
268    pub fn mi_zalloc_aligned(size: size_t, alignment: size_t) -> *mut c_void;
269    pub fn mi_calloc_aligned(count: size_t, size: size_t, alignment: size_t) -> *mut c_void;
270    pub fn mi_realloc_aligned(p: *mut c_void, newsize: size_t, alignment: size_t) -> *mut c_void;
271}
272
273// ── Process & thread lifecycle ──────────────────────────────────────────────
274
275unsafe extern "C" {
276    pub fn mi_collect(force: bool);
277    pub fn mi_thread_set_in_threadpool();
278    pub fn mi_version() -> c_int;
279    pub fn mi_process_info_print_out(out: Option<mi_output_fun>, arg: *mut c_void);
280    pub fn mi_process_info(
281        elapsed_msecs: *mut size_t,
282        user_msecs: *mut size_t,
283        system_msecs: *mut size_t,
284        current_rss: *mut size_t,
285        peak_rss: *mut size_t,
286        current_commit: *mut size_t,
287        peak_commit: *mut size_t,
288        page_faults: *mut size_t,
289    );
290}
291
292// ── Heaps ───────────────────────────────────────────────────────────────────
293
294unsafe extern "C" {
295    pub fn mi_heap_new() -> *mut mi_heap_t;
296    pub fn mi_heap_delete(heap: *mut mi_heap_t);
297    pub fn mi_heap_destroy(heap: *mut mi_heap_t);
298    pub fn mi_heap_collect(heap: *mut mi_heap_t, force: bool);
299    pub fn mi_heap_main() -> *mut mi_heap_t;
300    pub fn mi_heap_of(p: *const c_void) -> *mut mi_heap_t;
301    pub fn mi_heap_contains(heap: *const mi_heap_t, p: *const c_void) -> bool;
302    pub fn mi_heap_theap(heap: *mut mi_heap_t) -> *mut mi_theap_t;
303
304    pub fn mi_heap_malloc(heap: *mut mi_heap_t, size: size_t) -> *mut c_void;
305    pub fn mi_heap_zalloc(heap: *mut mi_heap_t, size: size_t) -> *mut c_void;
306    pub fn mi_heap_calloc(heap: *mut mi_heap_t, count: size_t, size: size_t) -> *mut c_void;
307    pub fn mi_heap_realloc(heap: *mut mi_heap_t, p: *mut c_void, newsize: size_t) -> *mut c_void;
308    pub fn mi_heap_malloc_aligned(
309        heap: *mut mi_heap_t,
310        size: size_t,
311        alignment: size_t,
312    ) -> *mut c_void;
313}
314
315// ── Thread-local heaps ──────────────────────────────────────────────────────
316
317unsafe extern "C" {
318    pub fn mi_theap_malloc(theap: *mut mi_theap_t, size: size_t) -> *mut c_void;
319    pub fn mi_theap_zalloc(theap: *mut mi_theap_t, size: size_t) -> *mut c_void;
320    pub fn mi_theap_malloc_small(theap: *mut mi_theap_t, size: size_t) -> *mut c_void;
321    pub fn mi_theap_zalloc_small(theap: *mut mi_theap_t, size: size_t) -> *mut c_void;
322    pub fn mi_theap_wmalloc_small(theap: *mut mi_theap_t, wsize: size_t) -> *mut c_void;
323    pub fn mi_theap_wzalloc_small(theap: *mut mi_theap_t, wsize: size_t) -> *mut c_void;
324}
325
326/// Allocate from a thread-local heap when the size is statically known.
327///
328/// This mirrors mimalloc's inline `mi_theap_malloc_csize` helper.
329///
330/// # Safety
331/// `theap` must be non-null and valid for the calling thread. The returned
332/// pointer must be checked for null and freed with a compatible mimalloc free
333/// API.
334#[inline]
335pub unsafe fn mi_theap_malloc_csize(theap: *mut mi_theap_t, size: size_t) -> *mut c_void {
336    if size <= MI_SMALL_SIZE_MAX {
337        unsafe { mi_theap_wmalloc_small(theap, mi_wsize_from_size(size)) }
338    } else {
339        unsafe { mi_theap_malloc(theap, size) }
340    }
341}
342
343/// Allocate zeroed memory from a thread-local heap when the size is statically known.
344///
345/// This mirrors mimalloc's inline `mi_theap_zalloc_csize` helper.
346///
347/// # Safety
348/// `theap` must be non-null and valid for the calling thread. The returned
349/// pointer must be checked for null and freed with a compatible mimalloc free
350/// API.
351#[inline]
352pub unsafe fn mi_theap_zalloc_csize(theap: *mut mi_theap_t, size: size_t) -> *mut c_void {
353    if size <= MI_SMALL_SIZE_MAX {
354        unsafe { mi_theap_wzalloc_small(theap, mi_wsize_from_size(size)) }
355    } else {
356        unsafe { mi_theap_zalloc(theap, size) }
357    }
358}
359
360// ── Arena management ────────────────────────────────────────────────────────
361
362unsafe extern "C" {
363    pub fn mi_reserve_os_memory_ex(
364        size: size_t,
365        commit: bool,
366        allow_large: bool,
367        exclusive: bool,
368        arena_id: *mut mi_arena_id_t,
369    ) -> c_int;
370    pub fn mi_manage_os_memory_ex(
371        start: *mut c_void,
372        size: size_t,
373        is_committed: bool,
374        is_pinned: bool,
375        is_zero: bool,
376        numa_node: c_int,
377        exclusive: bool,
378        arena_id: *mut mi_arena_id_t,
379    ) -> bool;
380    pub fn mi_arena_min_alignment() -> size_t;
381    pub fn mi_arena_min_size() -> size_t;
382    pub fn mi_arena_max_object_size() -> size_t;
383    pub fn mi_heap_new_in_arena(arena_id: mi_arena_id_t) -> *mut mi_heap_t;
384}
385
386// ── Options ─────────────────────────────────────────────────────────────────
387
388unsafe extern "C" {
389    pub fn mi_option_is_enabled(option: mi_option_t) -> bool;
390    pub fn mi_option_enable(option: mi_option_t);
391    pub fn mi_option_disable(option: mi_option_t);
392    pub fn mi_option_get(option: mi_option_t) -> c_long;
393    pub fn mi_option_get_size(option: mi_option_t) -> size_t;
394    pub fn mi_option_set(option: mi_option_t, value: c_long);
395}
396
397// ── Experimental profiling ─────────────────────────────────────────────────
398
399unsafe extern "C" {
400    pub fn mi_heap_profile(heap: *mut mi_heap_t, profiler: *mut mi_profiler_t) -> bool;
401    pub fn mi_heap_profile_disable(heap: *mut mi_heap_t);
402    pub fn mi_subproc_profile(subproc_id: mi_subproc_id_t, profiler: *mut mi_profiler_t) -> bool;
403    pub fn mi_profile(profiler: *mut mi_profiler_t) -> bool;
404    pub fn mi_profiler_start(profiler: *mut mi_profiler_t) -> bool;
405    pub fn mi_profiler_stop(profiler: *mut mi_profiler_t) -> bool;
406}
407
408// ── POSIX-compatible ────────────────────────────────────────────────────────
409
410unsafe extern "C" {
411    pub fn mi_posix_memalign(p: *mut *mut c_void, alignment: size_t, size: size_t) -> c_int;
412    pub fn mi_memalign(alignment: size_t, size: size_t) -> *mut c_void;
413    pub fn mi_malloc_size(p: *const c_void) -> size_t;
414    pub fn mi_malloc_usable_size(p: *const c_void) -> size_t;
415}
416
417// ── Statistics ──────────────────────────────────────────────────────────────
418
419unsafe extern "C" {
420    pub fn mi_stats_get_json(buf_size: size_t, buf: *mut c_char) -> *mut c_char;
421    pub fn mi_stats_print_out(out: Option<mi_output_fun>, arg: *mut c_void);
422    pub fn mi_stats_reset();
423    pub fn mi_heap_stats_get_json(
424        heap: *mut mi_heap_t,
425        buf_size: size_t,
426        buf: *mut c_char,
427    ) -> *mut c_char;
428    pub fn mi_heap_stats_print_out(
429        heap: *mut mi_heap_t,
430        out: Option<mi_output_fun>,
431        arg: *mut c_void,
432    );
433    pub fn mi_theap_stats_merge_to_heap(theap: *mut mi_theap_t);
434}