├── libmimalloc-sys ├── LICENSE.txt ├── .gitignore ├── sys-test │ ├── src │ │ └── main.rs │ ├── Cargo.toml │ └── build.rs ├── Cargo.toml ├── build.rs └── src │ ├── lib.rs │ └── extended.rs ├── .gitignore ├── .gitmodules ├── test-override-with-dylib ├── Cargo.toml ├── src │ ├── dep.c │ └── main.rs └── build.rs ├── LICENSE.txt ├── Cargo.toml ├── src ├── extended.rs └── lib.rs ├── README.md └── .github └── workflows └── ci.yml /libmimalloc-sys/LICENSE.txt: -------------------------------------------------------------------------------- 1 | ../LICENSE.txt -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | .DS_Store -------------------------------------------------------------------------------- /libmimalloc-sys/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock -------------------------------------------------------------------------------- /libmimalloc-sys/sys-test/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(bad_style, unused_imports, unused_macros, clippy::all)] 2 | 3 | use libmimalloc_sys::*; 4 | 5 | include!(concat!(env!("OUT_DIR"), "/all.rs")); 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libmimalloc-sys/c_src/mimalloc/v2"] 2 | path = libmimalloc-sys/c_src/mimalloc/v2 3 | url = https://github.com/microsoft/mimalloc 4 | [submodule "libmimalloc-sys/c_src/mimalloc/v3"] 5 | path = libmimalloc-sys/c_src/mimalloc/v3 6 | url = https://github.com/microsoft/mimalloc 7 | -------------------------------------------------------------------------------- /test-override-with-dylib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-override-with-dylib" 3 | version = "0.0.0" 4 | license = "MIT OR Apache-2.0" 5 | description = "A test helper for mimalloc" 6 | edition = "2018" 7 | publish = false 8 | 9 | [dependencies] 10 | libc = { version = "^0.2.8", default-features = false } 11 | libmimalloc-sys = { path = "../libmimalloc-sys" } 12 | 13 | [build-dependencies] 14 | cc = "^1.0.13" 15 | 16 | [features] 17 | override = ["libmimalloc-sys/override"] 18 | -------------------------------------------------------------------------------- /test-override-with-dylib/src/dep.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | 6 | const char* dep_lookup_malloc_address(void) { 7 | Dl_info info; 8 | if (!dladdr((void *)malloc, &info)) { 9 | printf("failed finding `malloc`\n"); 10 | abort(); 11 | } 12 | return info.dli_fname; 13 | } 14 | 15 | void* dep_malloc(size_t size) { 16 | return malloc(size); 17 | } 18 | 19 | void dep_free(void* ptr) { 20 | free(ptr); 21 | } 22 | -------------------------------------------------------------------------------- /libmimalloc-sys/sys-test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libmimalloc-sys-test" 3 | version = "0.1.0" 4 | authors = ["Thom Chiovoloni "] 5 | edition = "2018" 6 | description = "Bindings test for libmimalloc-sys" 7 | license = "MIT" 8 | publish = false 9 | 10 | [dependencies] 11 | libmimalloc-sys = { path = ".." } 12 | libc = "0.2" 13 | 14 | [build-dependencies] 15 | ctest2 = "0.4" 16 | 17 | [features] 18 | secure = ["libmimalloc-sys/secure"] 19 | extended = ["libmimalloc-sys/extended"] 20 | v3 = ["libmimalloc-sys/v3"] 21 | -------------------------------------------------------------------------------- /test-override-with-dylib/build.rs: -------------------------------------------------------------------------------- 1 | //! Build shared library `dep.c`. 2 | use std::{env, path::PathBuf}; 3 | 4 | fn main() { 5 | println!("cargo:rerun-if-changed=src/dep.c"); 6 | 7 | let out_dir = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); 8 | 9 | // NOTE: Only for testing, extension is wrong when cross-compiling. 10 | let dylib = out_dir.join(format!( 11 | "{}dep{}", 12 | env::consts::DLL_PREFIX, 13 | env::consts::DLL_SUFFIX 14 | )); 15 | 16 | let status = cc::Build::new() 17 | .get_compiler() 18 | .to_command() 19 | .arg("src/dep.c") 20 | .arg("-shared") 21 | .arg("-o") 22 | .arg(&dylib) 23 | .status() 24 | .unwrap(); 25 | assert!(status.success()); 26 | 27 | println!("cargo:rustc-link-lib=dylib=dep"); 28 | println!("cargo:rustc-link-search=native={}", out_dir.display()); 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2019 Octavian Oncescu 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mimalloc" 3 | version = "0.1.48" 4 | authors = [ 5 | "Octavian Oncescu ", 6 | "Vincent Rouillé ", 7 | "Thom Chiovoloni ", 8 | ] 9 | edition = "2018" 10 | repository = "https://github.com/purpleprotocol/mimalloc_rust" 11 | keywords = ["mimalloc", "allocator", "encrypted-heap", "performance"] 12 | categories = ["memory-management", "api-bindings"] 13 | description = "Performance and security oriented drop-in allocator" 14 | license = "MIT" 15 | readme = "README.md" 16 | 17 | [workspace] 18 | members = [ 19 | "libmimalloc-sys", 20 | "libmimalloc-sys/sys-test", 21 | "test-override-with-dylib", 22 | ] 23 | 24 | [badges] 25 | travis-ci = { repository = "purpleprotocol/mimalloc_rust" } 26 | 27 | [dependencies] 28 | libmimalloc-sys = { path = "libmimalloc-sys", version = "0.1.44", default-features = false } 29 | 30 | [features] 31 | default = [] 32 | secure = ["libmimalloc-sys/secure"] 33 | override = ["libmimalloc-sys/override"] 34 | debug = ["libmimalloc-sys/debug"] 35 | debug_in_debug = ["libmimalloc-sys/debug_in_debug"] 36 | local_dynamic_tls = ["libmimalloc-sys/local_dynamic_tls"] 37 | no_thp = ["libmimalloc-sys/no_thp"] 38 | extended = ["libmimalloc-sys/extended"] 39 | v3 = ["libmimalloc-sys/v3"] 40 | -------------------------------------------------------------------------------- /src/extended.rs: -------------------------------------------------------------------------------- 1 | use crate::MiMalloc; 2 | use core::ffi::c_void; 3 | 4 | impl MiMalloc { 5 | /// Get the mimalloc version. 6 | /// 7 | /// For mimalloc version 1.8.6, this will return 186. 8 | pub fn version(&self) -> u32 { 9 | unsafe { ffi::mi_version() as u32 } 10 | } 11 | 12 | /// Return the amount of available bytes in a memory block. 13 | /// 14 | /// # Safety 15 | /// `ptr` must point to a memory block allocated by mimalloc, or be null. 16 | #[inline] 17 | pub unsafe fn usable_size(&self, ptr: *const u8) -> usize { 18 | ffi::mi_usable_size(ptr as *const c_void) 19 | } 20 | } 21 | 22 | #[cfg(test)] 23 | mod test { 24 | use super::*; 25 | use core::alloc::GlobalAlloc; 26 | use core::alloc::Layout; 27 | 28 | #[test] 29 | fn it_gets_version() { 30 | let version = MiMalloc.version(); 31 | assert!(version != 0); 32 | } 33 | 34 | #[test] 35 | fn it_checks_usable_size() { 36 | unsafe { 37 | let layout = Layout::from_size_align(8, 8).unwrap(); 38 | let alloc = MiMalloc; 39 | 40 | let ptr = alloc.alloc(layout); 41 | let usable_size = alloc.usable_size(ptr); 42 | alloc.dealloc(ptr, layout); 43 | assert!(usable_size >= 8); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /libmimalloc-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libmimalloc-sys" 3 | version = "0.1.44" 4 | authors = ["Octavian Oncescu "] 5 | edition = "2018" 6 | repository = "https://github.com/purpleprotocol/mimalloc_rust/tree/master/libmimalloc-sys" 7 | keywords = ["allocator", "encrypted-heap", "performance"] 8 | categories = ["memory-management", "api-bindings"] 9 | description = "Sys crate wrapping the mimalloc allocator" 10 | license = "MIT" 11 | links = "mimalloc" 12 | exclude = [ 13 | "/c_src/mimalloc/v2/bin", 14 | "/c_src/mimalloc/v2/cmake", 15 | "/c_src/mimalloc/v2/doc", 16 | "/c_src/mimalloc/v2/docs", 17 | "/c_src/mimalloc/v2/ide", 18 | "/c_src/mimalloc/v2/test", 19 | "/c_src/mimalloc/v3/bin", 20 | "/c_src/mimalloc/v3/cmake", 21 | "/c_src/mimalloc/v3/doc", 22 | "/c_src/mimalloc/v3/docs", 23 | "/c_src/mimalloc/v3/ide", 24 | "/c_src/mimalloc/v3/test", 25 | ] 26 | 27 | [dependencies] 28 | cty = { version = "0.2", optional = true } 29 | libc = "0.2" 30 | 31 | [build-dependencies] 32 | cc = "1.0" 33 | 34 | [features] 35 | secure = [] 36 | debug = [] 37 | debug_in_debug = [] 38 | override = [] 39 | extended = ["cty"] 40 | arena = [] 41 | local_dynamic_tls = [] 42 | no_thp = [] 43 | v3 = [] 44 | 45 | # Show `extended` on docs.rs since it's the full API surface. 46 | [package.metadata.docs.rs] 47 | features = ["extended"] 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mimalloc Rust 2 | 3 | [![Latest Version]][crates.io] [![Documentation]][docs.rs] 4 | 5 | A drop-in global allocator wrapper around the [mimalloc](https://github.com/microsoft/mimalloc) allocator. 6 | Mimalloc is a general purpose, performance oriented allocator built by Microsoft. 7 | 8 | ## Usage 9 | 10 | ```rust 11 | use mimalloc::MiMalloc; 12 | 13 | #[global_allocator] 14 | static GLOBAL: MiMalloc = MiMalloc; 15 | ``` 16 | 17 | ## Requirements 18 | 19 | A __C__ compiler is required for building [mimalloc](https://github.com/microsoft/mimalloc) with cargo. 20 | 21 | ## Usage with secure mode 22 | 23 | Using secure mode adds guard pages, 24 | randomized allocation, encrypted free lists, etc. The performance penalty is usually 25 | around 10% according to [mimalloc](https://github.com/microsoft/mimalloc) 26 | own benchmarks. 27 | 28 | To enable secure mode, put in `Cargo.toml`: 29 | 30 | ```ini 31 | [dependencies] 32 | mimalloc = { version = "*", features = ["secure"] } 33 | ``` 34 | 35 | ## Usage with v3 36 | 37 | By default this library uses mimalloc `v2`. 38 | To enable `v3`, put in `Cargo.toml`: 39 | 40 | ```ini 41 | [dependencies] 42 | mimalloc = { version = "*", features = ["v3"] } 43 | ``` 44 | 45 | [crates.io]: https://crates.io/crates/mimalloc 46 | [Latest Version]: https://img.shields.io/crates/v/mimalloc.svg 47 | [Documentation]: https://docs.rs/mimalloc/badge.svg 48 | [docs.rs]: https://docs.rs/mimalloc 49 | -------------------------------------------------------------------------------- /test-override-with-dylib/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Test that when overriding, that the `malloc` and `free` symbols are 2 | //! interoperable, even across a dylib boundary. 3 | use core::ffi::{c_char, c_void, CStr}; 4 | 5 | // Make sure that `rustc` links this. 6 | use libmimalloc_sys as _; 7 | 8 | extern "C-unwind" { 9 | fn dep_lookup_malloc_address() -> *const c_char; 10 | fn dep_malloc(size: libc::size_t) -> *mut c_void; 11 | fn dep_free(ptr: *mut c_void); 12 | } 13 | 14 | fn lookup_malloc_address() -> *const c_char { 15 | unsafe { 16 | let mut info: libc::Dl_info = core::mem::zeroed(); 17 | let fnptr: unsafe extern "C" fn(libc::size_t) -> *mut c_void = libc::malloc; 18 | let fnptr = fnptr as *const c_void; 19 | if libc::dladdr(fnptr, &mut info) == 0 { 20 | libc::printf(b"failed finding `malloc`\n\0".as_ptr().cast()); 21 | libc::abort(); 22 | } 23 | info.dli_fname 24 | } 25 | } 26 | 27 | fn main() { 28 | // Check that pointers created with `malloc` in a dylib dependency can be 29 | // free'd with `free` here. 30 | let ptr = unsafe { libc::malloc(10) }; 31 | unsafe { dep_free(ptr) }; 32 | let ptr = unsafe { dep_malloc(10) }; 33 | unsafe { libc::free(ptr) }; 34 | 35 | // If overidden, test that the same is true for `mi_malloc` being 36 | // interoperable with `free`. 37 | if cfg!(feature = "override") { 38 | let ptr = unsafe { libmimalloc_sys::mi_malloc(10) }; 39 | unsafe { dep_free(ptr) }; 40 | let ptr = unsafe { libmimalloc_sys::mi_malloc(10) }; 41 | unsafe { libc::free(ptr) }; 42 | 43 | let ptr = unsafe { libc::malloc(10) }; 44 | unsafe { libmimalloc_sys::mi_free(ptr) }; 45 | let ptr = unsafe { dep_malloc(10) }; 46 | unsafe { libmimalloc_sys::mi_free(ptr) }; 47 | } 48 | 49 | // Extra check that the symbol was actually from the same place. 50 | let dep = unsafe { CStr::from_ptr(dep_lookup_malloc_address()) }; 51 | let here = unsafe { CStr::from_ptr(lookup_malloc_address()) }; 52 | 53 | if cfg!(target_vendor = "apple") { 54 | // macOS / Mach-O symbols are not overriden in dependencies, they are 55 | // hooked into with `zone_register`. 56 | assert_eq!( 57 | dep.to_str().unwrap(), 58 | "/usr/lib/system/libsystem_malloc.dylib" 59 | ); 60 | } else { 61 | assert_eq!(dep, here); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /libmimalloc-sys/sys-test/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | fn main() { 4 | let cargo_manifest_dir = env!("CARGO_MANIFEST_DIR"); 5 | let secure = if env::var("CARGO_FEATURE_SECURE").is_ok() { 6 | Some("secure") 7 | } else { 8 | None 9 | }; 10 | let extended = if env::var("CARGO_FEATURE_EXTENDED").is_ok() { 11 | Some("extended") 12 | } else { 13 | None 14 | }; 15 | let version = if env::var("CARGO_FEATURE_V3").is_ok() { 16 | "v3" 17 | } else { 18 | "v2" 19 | }; 20 | 21 | let mut cfg = ctest2::TestGenerator::new(); 22 | cfg.header("mimalloc.h") 23 | .include(format!( 24 | "{cargo_manifest_dir}/../c_src/mimalloc/{version}/include" 25 | )) 26 | .cfg("feature", secure) 27 | .cfg("feature", extended) 28 | .cfg("feature", (version == "v3").then_some("v3")) 29 | .fn_cname(|rust, link_name| link_name.unwrap_or(rust).to_string()) 30 | // ignore whether or not the option enum is signed. 31 | .skip_signededness(|c| c.ends_with("_t") || c.ends_with("_e")) 32 | .type_name(|ty, _is_struct, _is_union| { 33 | match ty { 34 | // Special cases. We do this to avoid having both 35 | // `mi_blah_{s,e}` and `mi_blah_t`. 36 | "mi_heap_area_t" => "struct mi_heap_area_s".into(), 37 | "mi_heap_t" => "struct mi_heap_s".into(), 38 | "mi_options_t" => "enum mi_options_e".into(), 39 | 40 | // This also works but requires we export `mi_heap_s` and similar 41 | // in addition, so we just hardcode the above. 42 | 43 | // t if t.ends_with("_s") => format!("struct {}", t), 44 | // t if t.ends_with("_e") => format!("enum {}", t), 45 | // t if t.ends_with("_t") => t.to_string(), 46 | 47 | // mimalloc defines it's callbacks with the pointer at the 48 | // location of use, e.g. `typedef ret mi_some_fun(a0 x, a1 y);` 49 | // and then uses `mi_some_fun *arg` as argument types, which 50 | // appears to upset ctest, which would prefer function pointers 51 | // be declared as pointers, so we clean things up for it. 52 | t if t.ends_with("_fun") => format!("{}*", t), 53 | 54 | t => t.to_string(), 55 | } 56 | }); 57 | 58 | cfg.generate("../src/lib.rs", "all.rs"); 59 | } 60 | -------------------------------------------------------------------------------- /libmimalloc-sys/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::path::Path; 3 | 4 | fn main() { 5 | let mut build = cc::Build::new(); 6 | 7 | let version = if env::var("CARGO_FEATURE_V3").is_ok() { 8 | "v3" 9 | } else { 10 | "v2" 11 | }; 12 | 13 | let cargo_manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"); 14 | let include_dir = Path::new(&cargo_manifest_dir) 15 | .join("c_src/mimalloc/") 16 | .join(version) 17 | .join("include") 18 | .to_str() 19 | .expect("include path is not valid UTF-8") 20 | .to_string(); 21 | // Make the include directory available to consumers via the `DEP_MIMALLOC_INCLUDE_DIR` 22 | // environment variable. 23 | println!("cargo:INCLUDE_DIR={include_dir}"); 24 | 25 | build.include(format!("c_src/mimalloc/{version}/include")); 26 | build.include(format!("c_src/mimalloc/{version}/src")); 27 | build.file(format!("c_src/mimalloc/{version}/src/static.c")); 28 | 29 | let target_os = env::var("CARGO_CFG_TARGET_OS").expect("target_os not defined!"); 30 | let target_family = env::var("CARGO_CFG_TARGET_FAMILY").expect("target_family not defined!"); 31 | let target_vendor = env::var("CARGO_CFG_TARGET_VENDOR").expect("target_vendor not defined!"); 32 | let target_arch = env::var("CARGO_CFG_TARGET_ARCH").expect("target_arch not defined!"); 33 | 34 | if target_family != "windows" { 35 | build.flag("-Wno-error=date-time"); 36 | } 37 | 38 | if env::var_os("CARGO_FEATURE_OVERRIDE").is_some() { 39 | // Overriding malloc is only available on windows in shared mode, but we 40 | // only ever build a static lib. 41 | if target_family != "windows" { 42 | build.define("MI_MALLOC_OVERRIDE", None); 43 | } 44 | if target_vendor == "apple" { 45 | build.define("MI_OSX_ZONE", Some("1")); 46 | build.define("MI_OSX_INTERPOSE", Some("1")); 47 | } 48 | } 49 | 50 | if env::var_os("CARGO_FEATURE_SECURE").is_some() { 51 | build.define("MI_SECURE", "4"); 52 | } 53 | 54 | let dynamic_tls = env::var("CARGO_FEATURE_LOCAL_DYNAMIC_TLS").is_ok(); 55 | 56 | if target_family == "unix" && target_os != "haiku" { 57 | if dynamic_tls { 58 | build.flag_if_supported("-ftls-model=local-dynamic"); 59 | } else { 60 | build.flag_if_supported("-ftls-model=initial-exec"); 61 | } 62 | } 63 | 64 | if (target_os == "linux" || target_os == "android") 65 | && env::var_os("CARGO_FEATURE_NO_THP").is_some() 66 | { 67 | build.define("MI_NO_THP", "1"); 68 | } 69 | 70 | if env::var_os("CARGO_FEATURE_DEBUG").is_some() 71 | || (env::var_os("CARGO_FEATURE_DEBUG_IN_DEBUG").is_some() && cfg!(debug_assertions)) 72 | { 73 | build.define("MI_DEBUG", "3"); 74 | build.define("MI_SHOW_ERRORS", "1"); 75 | } else { 76 | // Remove heavy debug assertions etc 77 | build.define("MI_DEBUG", "0"); 78 | } 79 | 80 | if build.get_compiler().is_like_msvc() { 81 | build.cpp(true); 82 | } 83 | 84 | build.compile("mimalloc"); 85 | 86 | // on armv6 we need to link with libatomic 87 | if target_os == "linux" && target_arch == "arm" { 88 | // Embrace the atomic capability library across various platforms. 89 | // For instance, on certain platforms, llvm has relocated the atomic of the arm32 architecture to libclang_rt.builtins.a 90 | // while some use libatomic.a, and others use libatomic_ops.a. 91 | let atomic_name = env::var("DEP_ATOMIC").unwrap_or("atomic".to_owned()); 92 | println!("cargo:rustc-link-lib={}", atomic_name); 93 | } 94 | 95 | // Link with libs needed on Windows 96 | if target_os == "windows" { 97 | // https://github.com/microsoft/mimalloc/blob/af21001f7a65eafb8fb16460b018ebf9d75e2ad8/CMakeLists.txt#L487 98 | let libs = ["psapi", "shell32", "user32", "advapi32", "bcrypt"]; 99 | 100 | for lib in libs { 101 | println!("cargo:rustc-link-lib={}", lib); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Octavian Oncescu 2 | 3 | #![no_std] 4 | 5 | //! A drop-in global allocator wrapper around the [mimalloc](https://github.com/microsoft/mimalloc) allocator. 6 | //! Mimalloc is a general purpose, performance oriented allocator built by Microsoft. 7 | //! 8 | //! ## Usage 9 | //! ```rust,ignore 10 | //! use mimalloc::MiMalloc; 11 | //! 12 | //! #[global_allocator] 13 | //! static GLOBAL: MiMalloc = MiMalloc; 14 | //! ``` 15 | //! 16 | //! ## Usage with secure mode 17 | //! Using secure mode adds guard pages, 18 | //! randomized allocation, encrypted free lists, etc. The performance penalty is usually 19 | //! around 10% according to [mimalloc's](https://github.com/microsoft/mimalloc) 20 | //! own benchmarks. 21 | //! 22 | //! To enable secure mode, put in `Cargo.toml`: 23 | //! ```rust,ignore 24 | //! [dependencies] 25 | //! mimalloc = { version = "*", features = ["secure"] } 26 | //! ``` 27 | 28 | extern crate libmimalloc_sys as ffi; 29 | 30 | #[cfg(feature = "extended")] 31 | mod extended; 32 | 33 | use core::alloc::{GlobalAlloc, Layout}; 34 | use core::ffi::c_void; 35 | use ffi::*; 36 | 37 | /// Drop-in mimalloc global allocator. 38 | /// 39 | /// ## Usage 40 | /// ```rust,ignore 41 | /// use mimalloc::MiMalloc; 42 | /// 43 | /// #[global_allocator] 44 | /// static GLOBAL: MiMalloc = MiMalloc; 45 | /// ``` 46 | pub struct MiMalloc; 47 | 48 | unsafe impl GlobalAlloc for MiMalloc { 49 | #[inline] 50 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 51 | mi_malloc_aligned(layout.size(), layout.align()) as *mut u8 52 | } 53 | 54 | #[inline] 55 | unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { 56 | mi_zalloc_aligned(layout.size(), layout.align()) as *mut u8 57 | } 58 | 59 | #[inline] 60 | unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { 61 | mi_free(ptr as *mut c_void); 62 | } 63 | 64 | #[inline] 65 | unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { 66 | mi_realloc_aligned(ptr as *mut c_void, new_size, layout.align()) as *mut u8 67 | } 68 | } 69 | 70 | #[cfg(test)] 71 | mod tests { 72 | use super::*; 73 | 74 | #[test] 75 | fn it_frees_allocated_memory() { 76 | unsafe { 77 | let layout = Layout::from_size_align(8, 8).unwrap(); 78 | let alloc = MiMalloc; 79 | 80 | let ptr = alloc.alloc(layout); 81 | alloc.dealloc(ptr, layout); 82 | } 83 | } 84 | 85 | #[test] 86 | fn it_frees_allocated_big_memory() { 87 | unsafe { 88 | let layout = Layout::from_size_align(1 << 20, 32).unwrap(); 89 | let alloc = MiMalloc; 90 | 91 | let ptr = alloc.alloc(layout); 92 | alloc.dealloc(ptr, layout); 93 | } 94 | } 95 | 96 | #[test] 97 | fn it_frees_zero_allocated_memory() { 98 | unsafe { 99 | let layout = Layout::from_size_align(8, 8).unwrap(); 100 | let alloc = MiMalloc; 101 | 102 | let ptr = alloc.alloc_zeroed(layout); 103 | alloc.dealloc(ptr, layout); 104 | } 105 | } 106 | 107 | #[test] 108 | fn it_frees_zero_allocated_big_memory() { 109 | unsafe { 110 | let layout = Layout::from_size_align(1 << 20, 32).unwrap(); 111 | let alloc = MiMalloc; 112 | 113 | let ptr = alloc.alloc_zeroed(layout); 114 | alloc.dealloc(ptr, layout); 115 | } 116 | } 117 | 118 | #[test] 119 | fn it_frees_reallocated_memory() { 120 | unsafe { 121 | let layout = Layout::from_size_align(8, 8).unwrap(); 122 | let alloc = MiMalloc; 123 | 124 | let ptr = alloc.alloc(layout); 125 | let ptr = alloc.realloc(ptr, layout, 16); 126 | alloc.dealloc(ptr, layout); 127 | } 128 | } 129 | 130 | #[test] 131 | fn it_frees_reallocated_big_memory() { 132 | unsafe { 133 | let layout = Layout::from_size_align(1 << 20, 32).unwrap(); 134 | let alloc = MiMalloc; 135 | 136 | let ptr = alloc.alloc(layout); 137 | let ptr = alloc.realloc(ptr, layout, 2 << 20); 138 | alloc.dealloc(ptr, layout); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | # Run daily to catch when Rust updates cause problems to happen. 7 | schedule: 8 | - cron: '00 01 * * *' 9 | 10 | jobs: 11 | rust: 12 | name: Rust 13 | 14 | strategy: 15 | matrix: 16 | os: [ubuntu-latest, macos-latest, windows-latest] 17 | toolchain: ["stable"] 18 | 19 | runs-on: ${{ matrix.os }} 20 | 21 | env: 22 | CARGO_INCREMENTAL: 0 23 | RUST_BACKTRACE: 1 24 | 25 | steps: 26 | - name: Checkout repository 27 | uses: actions/checkout@v4 28 | with: 29 | submodules: recursive 30 | 31 | - name: Install Rust toolchain 32 | uses: dtolnay/rust-toolchain@master 33 | with: 34 | toolchain: ${{ matrix.toolchain }} 35 | 36 | - name: Build (secure) 37 | run: cargo build --features secure 38 | 39 | - name: Test (secure) 40 | run: cargo test --features secure 41 | 42 | - name: Test libmimalloc-sys crate bindings (secure) 43 | run: cargo run --features libmimalloc-sys-test/secure -p libmimalloc-sys-test 44 | 45 | - name: Build (no secure) 46 | run: cargo build 47 | 48 | - name: Test (no secure) 49 | run: cargo test 50 | 51 | - name: Test libmimalloc-sys crate bindings (no secure) 52 | run: cargo run -p libmimalloc-sys-test 53 | 54 | - name: Build (extended) 55 | run: cargo build --features extended 56 | 57 | - name: Test (extended) 58 | run: cargo test --features extended 59 | 60 | - name: Test libmimalloc-sys crate bindings (extended) 61 | run: cargo run --features libmimalloc-sys-test/extended -p libmimalloc-sys-test 62 | 63 | - name: Build (v3, secure) 64 | run: cargo build --features v3,secure 65 | 66 | - name: Test (v3, secure) 67 | run: cargo test --features v3,secure 68 | 69 | - name: Test libmimalloc-sys crate bindings (v3, secure) 70 | run: cargo run --features libmimalloc-sys-test/v3,libmimalloc-sys-test/secure -p libmimalloc-sys-test 71 | 72 | - name: Build (v3, no secure) 73 | run: cargo build --features v3 74 | 75 | - name: Test (v3, no secure) 76 | run: cargo test --features v3 77 | 78 | - name: Test libmimalloc-sys crate bindings (v3, no secure) 79 | run: cargo run --features libmimalloc-sys-test/v3 -p libmimalloc-sys-test 80 | 81 | - name: Build (v3, extended) 82 | run: cargo build --features v3,extended 83 | 84 | - name: Test (v3, extended) 85 | run: cargo test --features v3,extended 86 | 87 | - name: Test libmimalloc-sys crate bindings (v3, extended) 88 | run: cargo run --features libmimalloc-sys-test/v3,libmimalloc-sys-test/extended -p libmimalloc-sys-test 89 | 90 | - name: Test override dylib 91 | if: ${{ !contains(matrix.os, 'windows') }} 92 | run: cargo run -ptest-override-with-dylib --features override 93 | 94 | lint: 95 | name: Rustfmt / Clippy 96 | runs-on: ubuntu-latest 97 | 98 | steps: 99 | - uses: actions/checkout@v4 100 | with: 101 | submodules: recursive 102 | 103 | - uses: dtolnay/rust-toolchain@stable 104 | with: 105 | components: rustfmt, clippy 106 | 107 | - name: Fmt 108 | run: cargo fmt --all -- --check 109 | 110 | - name: Clippy 111 | run: cargo clippy --workspace -- -D warnings 112 | 113 | # Detect cases where documentation links would be dead 114 | doc: 115 | name: Check documentation 116 | runs-on: ubuntu-latest 117 | steps: 118 | 119 | - uses: actions/checkout@v4 120 | with: 121 | submodules: recursive 122 | 123 | - uses: dtolnay/rust-toolchain@nightly 124 | 125 | # Note: We need to use nightly rust, and `cargo rustdoc` (yes, not `cargo 126 | # doc`) to actually get it to respect -D warnings... Using nightly also 127 | # gets us the nicer syntax for linking to functions, and is in-line with 128 | # what docs.rs uses. 129 | 130 | - name: 'Check documentation links in `mimalloc`' 131 | run: cargo rustdoc -- -D warnings 132 | 133 | - name: 'Check documentation links in `libmimalloc-sys`' 134 | run: cargo rustdoc -p libmimalloc-sys -- -D warnings 135 | -------------------------------------------------------------------------------- /libmimalloc-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | // Copyright 2019 Octavian Oncescu 3 | 4 | use core::ffi::c_void; 5 | 6 | extern crate libc; 7 | 8 | #[cfg(feature = "extended")] 9 | mod extended; 10 | #[cfg(feature = "extended")] 11 | pub use extended::*; 12 | 13 | extern "C" { 14 | /// Allocate zero-initialized `size` bytes. 15 | /// 16 | /// Returns a pointer to newly allocated zero-initialized memory, or null if 17 | /// out of memory. 18 | pub fn mi_zalloc(size: usize) -> *mut c_void; 19 | 20 | /// Allocate `size` bytes. 21 | /// 22 | /// Returns pointer to the allocated memory or null if out of memory. 23 | /// Returns a unique pointer if called with `size` 0. 24 | pub fn mi_malloc(size: usize) -> *mut c_void; 25 | 26 | /// Re-allocate memory to `newsize` bytes. 27 | /// 28 | /// Return pointer to the allocated memory or null if out of memory. If null 29 | /// is returned, the pointer `p` is not freed. Otherwise the original 30 | /// pointer is either freed or returned as the reallocated result (in case 31 | /// it fits in-place with the new size). 32 | /// 33 | /// If `p` is null, it behaves as [`mi_malloc`]. If `newsize` is larger than 34 | /// the original `size` allocated for `p`, the bytes after `size` are 35 | /// uninitialized. 36 | pub fn mi_realloc(p: *mut c_void, newsize: usize) -> *mut c_void; 37 | 38 | /// Allocate `size` bytes aligned by `alignment`, initialized to zero. 39 | /// 40 | /// Return pointer to the allocated memory or null if out of memory. 41 | /// 42 | /// Returns a unique pointer if called with `size` 0. 43 | pub fn mi_zalloc_aligned(size: usize, alignment: usize) -> *mut c_void; 44 | 45 | /// Allocate `size` bytes aligned by `alignment`. 46 | /// 47 | /// Return pointer to the allocated memory or null if out of memory. 48 | /// 49 | /// Returns a unique pointer if called with `size` 0. 50 | pub fn mi_malloc_aligned(size: usize, alignment: usize) -> *mut c_void; 51 | 52 | /// Re-allocate memory to `newsize` bytes, aligned by `alignment`. 53 | /// 54 | /// Return pointer to the allocated memory or null if out of memory. If null 55 | /// is returned, the pointer `p` is not freed. Otherwise the original 56 | /// pointer is either freed or returned as the reallocated result (in case 57 | /// it fits in-place with the new size). 58 | /// 59 | /// If `p` is null, it behaves as [`mi_malloc_aligned`]. If `newsize` is 60 | /// larger than the original `size` allocated for `p`, the bytes after 61 | /// `size` are uninitialized. 62 | pub fn mi_realloc_aligned(p: *mut c_void, newsize: usize, alignment: usize) -> *mut c_void; 63 | 64 | /// Free previously allocated memory. 65 | /// 66 | /// The pointer `p` must have been allocated before (or be null). 67 | pub fn mi_free(p: *mut c_void); 68 | } 69 | 70 | /// When using the `"override"` feature flag, the user wants us to globally 71 | /// override the system allocator. 72 | /// 73 | /// However, since we build and link `mimalloc` as a static library/archive, 74 | /// the linker may decide to not care about our overrides if it can't directly 75 | /// see references to the symbols, see the following link for details: 76 | /// 77 | /// 78 | /// This is problematic if `mimalloc` is used from a library that by itself 79 | /// doesn't allocate, yet invokes other shared libraries that do, since then 80 | /// the linker wouldn't see any references to `malloc`/`free`, and the symbols 81 | /// would not be overridden. 82 | /// 83 | /// To avoid this problem, we make sure that the allocator functions are 84 | /// visible to the linker. 85 | /// 86 | /// To avoid this problem, we reference `mi_malloc` in a `#[used]` static. 87 | /// This makes it known to `rustc`, which will create a reference to it in a 88 | /// `symbols.o` stub file that is later passed directly to the linker (instead 89 | /// of being in an archive). See the following link for details on how this 90 | /// works: 91 | /// 92 | /// NOTE: This works because `mimalloc` is compiled into a single object file 93 | /// in `static.c`. If it was split across multiple files, we'd need to 94 | /// reference each symbol. See also the comment at the top of `static.c`. 95 | /// 96 | /// NOTE: On macOS, mimalloc doesn't just override malloc/free, but also 97 | /// registers itself with the allocator's zone APIs in a ctor 98 | /// (`_mi_macos_override_malloc`, marked with `__attribute__((constructor))`). 99 | /// Similarly to above, for the Mach-O linker to actually consider ctors as 100 | /// "used" when defined in an archive member in a static library, so we need 101 | /// to explicitly reference something in the object file. The constructor 102 | /// symbol itself is static, so we can't get a reference to that, so instead 103 | /// we reference `mi_malloc` here too). 104 | #[cfg(feature = "override")] 105 | mod set_up_statics { 106 | use super::*; 107 | #[used] // Could be `#[used(linker)]` once stable 108 | static USED: unsafe extern "C" fn(usize) -> *mut c_void = mi_malloc; 109 | } 110 | 111 | #[cfg(test)] 112 | mod tests { 113 | use super::*; 114 | 115 | #[test] 116 | fn it_frees_memory_malloc() { 117 | let ptr = unsafe { mi_malloc_aligned(8, 8) } as *mut u8; 118 | unsafe { mi_free(ptr as *mut c_void) }; 119 | } 120 | 121 | #[test] 122 | fn it_frees_memory_zalloc() { 123 | let ptr = unsafe { mi_zalloc_aligned(8, 8) } as *mut u8; 124 | unsafe { mi_free(ptr as *mut c_void) }; 125 | } 126 | 127 | #[test] 128 | fn it_frees_memory_realloc() { 129 | let ptr = unsafe { mi_malloc_aligned(8, 8) } as *mut u8; 130 | let ptr = unsafe { mi_realloc_aligned(ptr as *mut c_void, 8, 8) } as *mut u8; 131 | unsafe { mi_free(ptr as *mut c_void) }; 132 | } 133 | 134 | #[cfg(all(feature = "override", target_vendor = "apple"))] 135 | #[test] 136 | fn mimalloc_and_libc_are_interoperable_when_overridden() { 137 | let ptr = unsafe { mi_malloc(42) }; 138 | unsafe { libc::free(ptr) }; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /libmimalloc-sys/src/extended.rs: -------------------------------------------------------------------------------- 1 | #![allow(nonstandard_style)] 2 | 3 | use core::ffi::c_void; 4 | 5 | use cty::{c_char, c_int, c_long, c_ulonglong}; 6 | 7 | /// The maximum number of bytes which may be used as an argument to a function 8 | /// in the `_small` family ([`mi_malloc_small`], [`mi_zalloc_small`], etc). 9 | pub const MI_SMALL_SIZE_MAX: usize = 128 * core::mem::size_of::<*mut c_void>(); 10 | 11 | extern "C" { 12 | /// Allocate `count` items of `size` length each. 13 | /// 14 | /// Returns `null` if `count * size` overflows or on out-of-memory. 15 | /// 16 | /// All items are initialized to zero. 17 | pub fn mi_calloc(count: usize, size: usize) -> *mut c_void; 18 | 19 | /// Allocate `count` items of `size` length each. 20 | /// 21 | /// Returns `null` if `count * size` overflows or on out-of-memory, 22 | /// otherwise returns the same as [`mi_malloc(count * 23 | /// size)`](crate::mi_malloc). 24 | /// Equivalent to [`mi_calloc`], but returns uninitialized (and not zeroed) 25 | /// bytes. 26 | pub fn mi_mallocn(count: usize, size: usize) -> *mut c_void; 27 | 28 | /// Re-allocate memory to `count` elements of `size` bytes. 29 | /// 30 | /// The realloc equivalent of the [`mi_mallocn`] interface. Returns `null` 31 | /// if `count * size` overflows or on out-of-memory, otherwise returns the 32 | /// same as [`mi_realloc(p, count * size)`](crate::mi_realloc). 33 | pub fn mi_reallocn(p: *mut c_void, count: usize, size: usize) -> *mut c_void; 34 | 35 | /// Try to re-allocate memory to `newsize` bytes _in place_. 36 | /// 37 | /// Returns null on out-of-memory or if the memory could not be expanded in 38 | /// place. On success, returns the same pointer as `p`. 39 | /// 40 | /// If `newsize` is larger than the original `size` allocated for `p`, the 41 | /// bytes after `size` are uninitialized. 42 | /// 43 | /// If null is returned, the original pointer is not freed. 44 | /// 45 | /// Note: Conceptually, this is a realloc-like which returns null if it 46 | /// would be forced to reallocate memory and copy. In practice it's 47 | /// equivalent testing against [`mi_usable_size`](crate::mi_usable_size). 48 | pub fn mi_expand(p: *mut c_void, newsize: usize) -> *mut c_void; 49 | 50 | /// Re-allocate memory to `newsize` bytes. 51 | /// 52 | /// This differs from [`mi_realloc`](crate::mi_realloc) in that on failure, 53 | /// `p` is freed. 54 | pub fn mi_reallocf(p: *mut c_void, newsize: usize) -> *mut c_void; 55 | 56 | /// Allocate and duplicate a nul-terminated C string. 57 | /// 58 | /// This can be useful for Rust code when interacting with the FFI. 59 | pub fn mi_strdup(s: *const c_char) -> *mut c_char; 60 | 61 | /// Allocate and duplicate a nul-terminated C string, up to `n` bytes. 62 | /// 63 | /// This can be useful for Rust code when interacting with the FFI. 64 | pub fn mi_strndup(s: *const c_char, n: usize) -> *mut c_char; 65 | 66 | /// Resolve a file path name, producing a `C` string which can be passed to 67 | /// [`mi_free`](crate::mi_free). 68 | /// 69 | /// `resolved_name` should be null, but can also point to a buffer of at 70 | /// least `PATH_MAX` bytes. 71 | /// 72 | /// If successful, returns a pointer to the resolved absolute file name, or 73 | /// `null` on failure (with `errno` set to the error code). 74 | /// 75 | /// If `resolved_name` was `null`, the returned result should be freed with 76 | /// [`mi_free`](crate::mi_free). 77 | /// 78 | /// This can rarely be useful in FFI code, but is mostly included for 79 | /// completeness. 80 | pub fn mi_realpath(fname: *const c_char, resolved_name: *mut c_char) -> *mut c_char; 81 | 82 | /// Allocate `size * count` bytes aligned by `alignment`. 83 | /// 84 | /// Return pointer to the allocated memory or null if out of memory or if 85 | /// `size * count` overflows. 86 | /// 87 | /// Returns a unique pointer if called with `size * count` 0. 88 | pub fn mi_calloc_aligned(count: usize, size: usize, alignment: usize) -> *mut c_void; 89 | 90 | /// Allocate `size` bytes aligned by `alignment` at a specified `offset`. 91 | /// 92 | /// Note that the resulting pointer itself is not aligned by the alignment, 93 | /// but after `offset` bytes it will be. This can be useful for allocating 94 | /// data with an inline header, where the data has a specific alignment 95 | /// requirement. 96 | /// 97 | /// Specifically, if `p` is the returned pointer `p.add(offset)` is aligned 98 | /// to `alignment`. 99 | pub fn mi_malloc_aligned_at(size: usize, alignment: usize, offset: usize) -> *mut c_void; 100 | 101 | /// Allocate `size` bytes aligned by `alignment` at a specified `offset`, 102 | /// zero-initialized. 103 | /// 104 | /// This is a [`mi_zalloc`](crate::mi_zalloc) equivalent of [`mi_malloc_aligned_at`]. 105 | pub fn mi_zalloc_aligned_at(size: usize, alignment: usize, offset: usize) -> *mut c_void; 106 | 107 | /// Allocate `size` of bytes aligned by `alignment` and place the address of the 108 | /// allocated memory to `ptr`. 109 | /// 110 | /// Returns zero on success, invalid argument for invalid alignment, or out-of-memory. 111 | pub fn mi_posix_memalign(ptr: *mut *mut c_void, alignment: usize, size: usize) -> c_int; 112 | 113 | /// Allocate `size` bytes aligned by `alignment` with alignment as the first 114 | /// parameter. 115 | /// 116 | /// Return pointer to the allocated memory or null if out of memory. 117 | pub fn mi_aligned_alloc(alignment: usize, size: usize) -> *mut c_void; 118 | 119 | /// Allocate `size * count` bytes aligned by `alignment` at a specified 120 | /// `offset`, zero-initialized. 121 | /// 122 | /// This is a [`calloc`](crate::mi_calloc) equivalent of [`mi_malloc_aligned_at`]. 123 | pub fn mi_calloc_aligned_at( 124 | count: usize, 125 | size: usize, 126 | alignment: usize, 127 | offset: usize, 128 | ) -> *mut c_void; 129 | 130 | /// Re-allocate memory to `newsize` bytes aligned by `alignment` at a 131 | /// specified `offset`. 132 | /// 133 | /// This is a [`realloc`](crate::mi_realloc) equivalent of [`mi_malloc_aligned_at`]. 134 | pub fn mi_realloc_aligned_at( 135 | p: *mut c_void, 136 | newsize: usize, 137 | alignment: usize, 138 | offset: usize, 139 | ) -> *mut c_void; 140 | 141 | /// Zero initialized [re-allocation](crate::mi_realloc). 142 | /// 143 | /// In general, only valid on memory originally allocated by zero 144 | /// initialization: [`mi_calloc`](crate::mi_calloc), 145 | /// [`mi_zalloc`](crate::mi_zalloc), 146 | /// [`mi_zalloc_aligned`](crate::mi_zalloc_aligned), ... 147 | pub fn mi_rezalloc(p: *mut c_void, newsize: usize) -> *mut c_void; 148 | 149 | /// Zero initialized [re-allocation](crate::mi_realloc), following `calloc` 150 | /// paramater conventions. 151 | /// 152 | /// In general, only valid on memory originally allocated by zero 153 | /// initialization: [`mi_calloc`](crate::mi_calloc), 154 | /// [`mi_zalloc`](crate::mi_zalloc), 155 | /// [`mi_zalloc_aligned`](crate::mi_zalloc_aligned), ... 156 | pub fn mi_recalloc(p: *mut c_void, newcount: usize, size: usize) -> *mut c_void; 157 | 158 | /// Aligned version of [`mi_rezalloc`]. 159 | pub fn mi_rezalloc_aligned(p: *mut c_void, newsize: usize, alignment: usize) -> *mut c_void; 160 | 161 | /// Offset-aligned version of [`mi_rezalloc`]. 162 | pub fn mi_rezalloc_aligned_at( 163 | p: *mut c_void, 164 | newsize: usize, 165 | alignment: usize, 166 | offset: usize, 167 | ) -> *mut c_void; 168 | 169 | /// Aligned version of [`mi_recalloc`]. 170 | pub fn mi_recalloc_aligned( 171 | p: *mut c_void, 172 | newcount: usize, 173 | size: usize, 174 | alignment: usize, 175 | ) -> *mut c_void; 176 | 177 | /// Offset-aligned version of [`mi_recalloc`]. 178 | pub fn mi_recalloc_aligned_at( 179 | p: *mut c_void, 180 | newcount: usize, 181 | size: usize, 182 | alignment: usize, 183 | offset: usize, 184 | ) -> *mut c_void; 185 | 186 | /// Allocate an object of no more than [`MI_SMALL_SIZE_MAX`] bytes. 187 | /// 188 | /// Does not check that `size` is indeed small. 189 | /// 190 | /// Note: Currently [`mi_malloc`](crate::mi_malloc) checks if `size` is 191 | /// small and calls this if 192 | /// so at runtime, so its' only worth using if you know for certain. 193 | pub fn mi_malloc_small(size: usize) -> *mut c_void; 194 | 195 | /// Allocate an zero-initialized object of no more than 196 | /// [`MI_SMALL_SIZE_MAX`] bytes. 197 | /// 198 | /// Does not check that `size` is indeed small. 199 | /// 200 | /// Note: Currently [`mi_zalloc`](crate::mi_zalloc) checks if `size` is 201 | /// small and calls this if so at runtime, so its' only worth using if you 202 | /// know for certain. 203 | pub fn mi_zalloc_small(size: usize) -> *mut c_void; 204 | 205 | /// Return the available bytes in a memory block. 206 | /// 207 | /// The returned size can be used to call `mi_expand` successfully. 208 | pub fn mi_usable_size(p: *const c_void) -> usize; 209 | 210 | /// Return the used allocation size. 211 | /// 212 | /// Returns the size `n` that will be allocated, where `n >= size`. 213 | /// 214 | /// Generally, `mi_usable_size(mi_malloc(size)) == mi_good_size(size)`. This 215 | /// can be used to reduce internal wasted space when allocating buffers for 216 | /// example. 217 | /// 218 | /// See [`mi_usable_size`](crate::mi_usable_size). 219 | pub fn mi_good_size(size: usize) -> usize; 220 | 221 | /// Eagerly free memory. 222 | /// 223 | /// If `force` is true, aggressively return memory to the OS (can be 224 | /// expensive!) 225 | /// 226 | /// Regular code should not have to call this function. It can be beneficial 227 | /// in very narrow circumstances; in particular, when a long running thread 228 | /// allocates a lot of blocks that are freed by other threads it may improve 229 | /// resource usage by calling this every once in a while. 230 | pub fn mi_collect(force: bool); 231 | 232 | /// Checked free: If `p` came from mimalloc's heap (as decided by 233 | /// [`mi_is_in_heap_region`]), this is [`mi_free(p)`](crate::mi_free), but 234 | /// otherwise it is a no-op. 235 | pub fn mi_cfree(p: *mut c_void); 236 | 237 | /// Returns true if this is a pointer into a memory region that has been 238 | /// reserved by the mimalloc heap. 239 | /// 240 | /// This function is described by the mimalloc documentation as "relatively 241 | /// fast". 242 | /// 243 | /// See also [`mi_heap_check_owned`], which is (much) slower and slightly 244 | /// more precise, but only concerns a single `mi_heap`. 245 | pub fn mi_is_in_heap_region(p: *const c_void) -> bool; 246 | 247 | /// Layout-aware deallocation: Like [`mi_free`](crate::mi_free), but accepts 248 | /// the size and alignment as well. 249 | /// 250 | /// Note: unlike some allocators that require this information for 251 | /// performance, mimalloc doesn't need it (as of the current version, 252 | /// v2.0.0), and so it currently implements this as a (debug) assertion that 253 | /// verifies that `p` is actually aligned to `alignment` and is usable for 254 | /// at least `size` bytes, before delegating to `mi_free`. 255 | /// 256 | /// However, currently there's no way to have this crate enable mimalloc's 257 | /// debug assertions, so these checks aren't particularly useful. 258 | /// 259 | /// Note: It's legal to pass null to this function, and you are not required 260 | /// to use this to deallocate memory from an aligned allocation function. 261 | pub fn mi_free_size_aligned(p: *mut c_void, size: usize, alignment: usize); 262 | 263 | /// Size-aware deallocation: Like [`mi_free`](crate::mi_free), but accepts 264 | /// the size and alignment as well. 265 | /// 266 | /// Note: unlike some allocators that require this information for 267 | /// performance, mimalloc doesn't need it (as of the current version, 268 | /// v2.0.0), and so it currently implements this as a (debug) assertion that 269 | /// verifies that `p` is actually aligned to `alignment` and is usable for 270 | /// at least `size` bytes, before delegating to `mi_free`. 271 | /// 272 | /// However, currently there's no way to have this crate enable mimalloc's 273 | /// debug assertions, so these checks aren't particularly useful. 274 | /// 275 | /// Note: It's legal to pass null to this function. 276 | pub fn mi_free_size(p: *mut c_void, size: usize); 277 | 278 | /// Alignment-aware deallocation: Like [`mi_free`](crate::mi_free), but 279 | /// accepts the size and alignment as well. 280 | /// 281 | /// Note: unlike some allocators that require this information for 282 | /// performance, mimalloc doesn't need it (as of the current version, 283 | /// v2.0.0), and so it currently implements this as a (debug) assertion that 284 | /// verifies that `p` is actually aligned to `alignment` and is usable for 285 | /// at least `size` bytes, before delegating to `mi_free`. 286 | /// 287 | /// However, currently there's no way to have this crate enable mimalloc's 288 | /// debug assertions, so these checks aren't particularly useful. 289 | /// 290 | /// Note: It's legal to pass null to this function. 291 | pub fn mi_free_aligned(p: *mut c_void, alignment: usize); 292 | 293 | /// Print the main statistics. 294 | /// 295 | /// Ignores the passed in argument, and outputs to the registered output 296 | /// function or stderr by default. 297 | /// 298 | /// Most detailed when using a debug build. 299 | pub fn mi_stats_print(_: *mut c_void); 300 | 301 | /// Print the main statistics. 302 | /// 303 | /// Pass `None` for `out` to use the default. If `out` is provided, `arc` is 304 | /// passed as it's second parameter. 305 | /// 306 | /// Most detailed when using a debug build. 307 | pub fn mi_stats_print_out(out: mi_output_fun, arg: *mut c_void); 308 | 309 | /// Reset statistics. 310 | /// 311 | /// Note: This function is thread safe. 312 | pub fn mi_stats_reset(); 313 | 314 | /// Merge thread local statistics with the main statistics and reset. 315 | /// 316 | /// Note: This function is thread safe. 317 | pub fn mi_stats_merge(); 318 | 319 | /// Return the mimalloc version number. 320 | /// 321 | /// For example version 1.6.3 would return the number `163`. 322 | pub fn mi_version() -> c_int; 323 | 324 | /// Initialize mimalloc on a thread. 325 | /// 326 | /// Should not be used as on most systems (pthreads, windows) this is done 327 | /// automatically. 328 | pub fn mi_thread_init(); 329 | 330 | /// Initialize the process. 331 | /// 332 | /// Should not be used on most systems, as it's called by thread_init or the 333 | /// process loader. 334 | pub fn mi_process_init(); 335 | 336 | /// Return process information (time and memory usage). All parameters are 337 | /// optional (nullable) out-params: 338 | /// 339 | /// | Parameter | Description | 340 | /// | :- | :- | 341 | /// | `elapsed_msecs` | Elapsed wall-clock time of the process in milli-seconds. | 342 | /// | `user_msecs` | User time in milli-seconds (as the sum over all threads). | 343 | /// | `system_msecs` | System time in milli-seconds. | 344 | /// | `current_rss` | Current working set size (touched pages). | 345 | /// | `peak_rss` | Peak working set size (touched pages). | 346 | /// | `current_commit` | Current committed memory (backed by the page file). | 347 | /// | `peak_commit` | Peak committed memory (backed by the page file). | 348 | /// | `page_faults` | Count of hard page faults. | 349 | /// 350 | /// The `current_rss` is precise on Windows and MacOSX; other systems 351 | /// estimate this using `current_commit`. The `commit` is precise on Windows 352 | /// but estimated on other systems as the amount of read/write accessible 353 | /// memory reserved by mimalloc. 354 | pub fn mi_process_info( 355 | elapsed_msecs: *mut usize, 356 | user_msecs: *mut usize, 357 | system_msecs: *mut usize, 358 | current_rss: *mut usize, 359 | peak_rss: *mut usize, 360 | current_commit: *mut usize, 361 | peak_commit: *mut usize, 362 | page_faults: *mut usize, 363 | ); 364 | 365 | /// Uninitialize mimalloc on a thread. 366 | /// 367 | /// Should not be used as on most systems (pthreads, windows) this is done 368 | /// automatically. Ensures that any memory that is not freed yet (but will 369 | /// be freed by other threads in the future) is properly handled. 370 | /// 371 | /// Note: This function is thread safe. 372 | pub fn mi_thread_done(); 373 | 374 | /// Print out heap statistics for this thread. 375 | /// 376 | /// Pass `None` for `out` to use the default. If `out` is provided, `arc` is 377 | /// passed as it's second parameter 378 | /// 379 | /// Most detailed when using a debug build. 380 | /// 381 | /// Note: This function is thread safe. 382 | pub fn mi_thread_stats_print_out(out: mi_output_fun, arg: *mut c_void); 383 | 384 | /// Register an output function. 385 | /// 386 | /// - `out` The output function, use `None` to output to stderr. 387 | /// - `arg` Argument that will be passed on to the output function. 388 | /// 389 | /// The `out` function is called to output any information from mimalloc, 390 | /// like verbose or warning messages. 391 | /// 392 | /// Note: This function is thread safe. 393 | pub fn mi_register_output(out: mi_output_fun, arg: *mut c_void); 394 | 395 | /// Register a deferred free function. 396 | /// 397 | /// - `deferred_free` Address of a deferred free-ing function or `None` to 398 | /// unregister. 399 | /// - `arg` Argument that will be passed on to the deferred free function. 400 | /// 401 | /// Some runtime systems use deferred free-ing, for example when using 402 | /// reference counting to limit the worst case free time. 403 | /// 404 | /// Such systems can register (re-entrant) deferred free function to free 405 | /// more memory on demand. 406 | /// 407 | /// - When the `force` parameter is `true` all possible memory should be 408 | /// freed. 409 | /// 410 | /// - The per-thread `heartbeat` parameter is monotonically increasing and 411 | /// guaranteed to be deterministic if the program allocates 412 | /// deterministically. 413 | /// 414 | /// - The `deferred_free` function is guaranteed to be called 415 | /// deterministically after some number of allocations (regardless of 416 | /// freeing or available free memory). 417 | /// 418 | /// At most one `deferred_free` function can be active. 419 | /// 420 | /// Note: This function is thread safe. 421 | pub fn mi_register_deferred_free(out: mi_deferred_free_fun, arg: *mut c_void); 422 | 423 | /// Register an error callback function. 424 | /// 425 | /// The `errfun` function is called on an error in mimalloc after emitting 426 | /// an error message (through the output function). 427 | /// 428 | /// It as always legal to just return from the `errfun` function in which 429 | /// case allocation functions generally return null or ignore the condition. 430 | /// 431 | /// The default function only calls abort() when compiled in secure mode 432 | /// with an `EFAULT` error. The possible error codes are: 433 | /// 434 | /// - `EAGAIN` (11): Double free was detected (only in debug and secure 435 | /// mode). 436 | /// - `EFAULT` (14): Corrupted free list or meta-data was detected (only in 437 | /// debug and secure mode). 438 | /// - `ENOMEM` (12): Not enough memory available to satisfy the request. 439 | /// - `EOVERFLOW` (75): Too large a request, for example in `mi_calloc`, the 440 | /// `count` and `size` parameters are too large. 441 | /// - `EINVAL` (22): Trying to free or re-allocate an invalid pointer. 442 | /// 443 | /// Note: This function is thread safe. 444 | pub fn mi_register_error(out: mi_error_fun, arg: *mut c_void); 445 | } 446 | 447 | /// An output callback. Must be thread-safe. 448 | /// 449 | /// See [`mi_stats_print_out`], [`mi_thread_stats_print_out`], [`mi_register_output`] 450 | pub type mi_output_fun = Option; 451 | 452 | /// Type of deferred free functions. Must be thread-safe. 453 | /// 454 | /// - `force`: If true, all outstanding items should be freed. 455 | /// - `heartbeat` A monotonically increasing count. 456 | /// - `arg` Argument that was passed at registration to hold extra state. 457 | /// 458 | /// See [`mi_register_deferred_free`] 459 | pub type mi_deferred_free_fun = 460 | Option; 461 | 462 | /// Type of error callback functions. Must be thread-safe. 463 | /// 464 | /// - `err`: Error code (see [`mi_register_error`] for a list). 465 | /// - `arg`: Argument that was passed at registration to hold extra state. 466 | /// 467 | /// See [`mi_register_error`] 468 | pub type mi_error_fun = Option; 469 | 470 | /// Runtime options. All options are false by default. 471 | pub type mi_option_t = c_int; 472 | 473 | #[cfg(feature = "arena")] 474 | /// Arena Id 475 | pub type mi_arena_id_t = c_int; 476 | 477 | // Note: mimalloc doc website seems to have the order of show_stats and 478 | // show_errors reversed as of 1.6.3, however what I have here is correct: 479 | // https://github.com/microsoft/mimalloc/issues/266#issuecomment-653822341 480 | 481 | /// Print error messages to `stderr`. 482 | pub const mi_option_show_errors: mi_option_t = 0; 483 | 484 | /// Print statistics to `stderr` when the program is done. 485 | pub const mi_option_show_stats: mi_option_t = 1; 486 | 487 | /// Print verbose messages to `stderr`. 488 | pub const mi_option_verbose: mi_option_t = 2; 489 | 490 | /// ### The following options are experimental 491 | /// 492 | /// Option (experimental) Use large OS pages (2MiB in size) if possible. 493 | /// 494 | /// Use large OS pages (2MiB) when available; for some workloads this can 495 | /// significantly improve performance. Use mi_option_verbose to check if 496 | /// the large OS pages are enabled -- usually one needs to explicitly allow 497 | /// large OS pages (as on Windows and Linux). However, sometimes the OS is 498 | /// very slow to reserve contiguous physical memory for large OS pages so 499 | /// use with care on systems that can have fragmented memory (for that 500 | /// reason, we generally recommend to use mi_option_reserve_huge_os_pages 501 | /// instead whenever possible). 502 | pub const mi_option_large_os_pages: mi_option_t = 6; 503 | 504 | /// Option (experimental) The number of huge OS pages (1GiB in size) to reserve at the start of the program. 505 | /// 506 | /// This reserves the huge pages at startup and sometimes this can give a large (latency) performance 507 | /// improvement on big workloads. Usually it is better to not use MIMALLOC_LARGE_OS_PAGES in 508 | /// combination with this setting. Just like large OS pages, use with care as reserving contiguous 509 | /// physical memory can take a long time when memory is fragmented (but reserving the huge pages is 510 | /// done at startup only once). Note that we usually need to explicitly enable huge OS pages (as on 511 | /// Windows and Linux)). With huge OS pages, it may be beneficial to set the setting 512 | /// mi_option_eager_commit_delay=N (N is 1 by default) to delay the initial N segments (of 4MiB) of 513 | /// a thread to not allocate in the huge OS pages; this prevents threads that are short lived and 514 | /// allocate just a little to take up space in the huge OS page area (which cannot be reset). 515 | pub const mi_option_reserve_huge_os_pages: mi_option_t = 7; 516 | 517 | /// Option (experimental) Reserve huge OS pages at node N. 518 | /// 519 | /// The huge pages are usually allocated evenly among NUMA nodes. 520 | /// You can use mi_option_reserve_huge_os_pages_at=N where `N` is the numa node (starting at 0) to allocate all 521 | /// the huge pages at a specific numa node instead. 522 | pub const mi_option_reserve_huge_os_pages_at: mi_option_t = 8; 523 | 524 | /// Option (experimental) Reserve specified amount of OS memory at startup, e.g. "1g" or "512m". 525 | pub const mi_option_reserve_os_memory: mi_option_t = 9; 526 | 527 | /// Option (experimental) the first N segments per thread are not eagerly committed (=1). 528 | pub const mi_option_eager_commit_delay: mi_option_t = 14; 529 | 530 | /// Option (experimental) Pretend there are at most N NUMA nodes; Use 0 to use the actual detected NUMA nodes at runtime. 531 | pub const mi_option_use_numa_nodes: mi_option_t = 16; 532 | 533 | /// Option (experimental) If set to 1, do not use OS memory for allocation (but only pre-reserved arenas) 534 | pub const mi_option_limit_os_alloc: mi_option_t = 17; 535 | 536 | /// Option (experimental) OS tag to assign to mimalloc'd memory 537 | pub const mi_option_os_tag: mi_option_t = 18; 538 | 539 | /// Option (experimental) 540 | pub const mi_option_max_errors: mi_option_t = 19; 541 | 542 | /// Option (experimental) 543 | pub const mi_option_max_warnings: mi_option_t = 20; 544 | 545 | #[cfg(not(feature = "v3"))] 546 | /// Option (experimental) 547 | pub const mi_option_max_segment_reclaim: mi_option_t = 21; 548 | 549 | /// Last option. 550 | #[cfg(not(feature = "v3"))] 551 | pub const _mi_option_last: mi_option_t = 37; 552 | #[cfg(feature = "v3")] 553 | pub const _mi_option_last: mi_option_t = 43; 554 | 555 | extern "C" { 556 | // Note: mi_option_{enable,disable} aren't exposed because they're redundant 557 | // and because of https://github.com/microsoft/mimalloc/issues/266. 558 | 559 | /// Returns true if the provided option is enabled. 560 | /// 561 | /// Note: this function is not thread safe. 562 | pub fn mi_option_is_enabled(option: mi_option_t) -> bool; 563 | 564 | /// Enable or disable the given option. 565 | /// 566 | /// Note: this function is not thread safe. 567 | pub fn mi_option_set_enabled(option: mi_option_t, enable: bool); 568 | 569 | /// If the given option has not yet been initialized with [`mi_option_set`] 570 | /// or [`mi_option_set_enabled`], enables or disables the option. If it has, 571 | /// this function does nothing. 572 | /// 573 | /// Note: this function is not thread safe. 574 | pub fn mi_option_set_enabled_default(option: mi_option_t, enable: bool); 575 | 576 | /// Returns the value of the provided option. 577 | /// 578 | /// The value of boolean options is 1 or 0, however experimental options 579 | /// exist which take a numeric value, which is the intended use of this 580 | /// function. 581 | /// 582 | /// These options are not exposed as constants for stability reasons, 583 | /// however you can still use them as arguments to this and other 584 | /// `mi_option_` functions if needed, see the mimalloc documentation for 585 | /// details: https://microsoft.github.io/mimalloc/group__options.html 586 | /// 587 | /// Note: this function is not thread safe. 588 | pub fn mi_option_get(option: mi_option_t) -> c_long; 589 | 590 | /// Set the option to the given value. 591 | /// 592 | /// The value of boolean options is 1 or 0, however experimental options 593 | /// exist which take a numeric value, which is the intended use of this 594 | /// function. 595 | /// 596 | /// These options are not exposed as constants for stability reasons, 597 | /// however you can still use them as arguments to this and other 598 | /// `mi_option_` functions if needed, 599 | /// 600 | /// Note: this function is not thread safe. 601 | pub fn mi_option_set(option: mi_option_t, value: c_long); 602 | 603 | /// If the given option has not yet been initialized with [`mi_option_set`] 604 | /// or [`mi_option_set_enabled`], sets the option to the given value. If it 605 | /// has, this function does nothing. 606 | /// 607 | /// The value of boolean options is 1 or 0, however experimental options 608 | /// exist which take a numeric value, which is the intended use of this 609 | /// function. 610 | /// 611 | /// These options are not exposed as constants for stability reasons, 612 | /// however you can still use them as arguments to this and other 613 | /// `mi_option_` functions if needed. 614 | /// 615 | /// Note: this function is not thread safe. 616 | pub fn mi_option_set_default(option: mi_option_t, value: c_long); 617 | } 618 | 619 | /// First-class heaps that can be destroyed in one go. 620 | /// 621 | /// Note: The pointers allocated out of a heap can be be freed using 622 | /// [`mi_free`](crate::mi_free) -- there is no `mi_heap_free`. 623 | /// 624 | /// # Example 625 | /// 626 | /// ``` 627 | /// use libmimalloc_sys as mi; 628 | /// unsafe { 629 | /// let h = mi::mi_heap_new(); 630 | /// assert!(!h.is_null()); 631 | /// let p = mi::mi_heap_malloc(h, 50); 632 | /// assert!(!p.is_null()); 633 | /// 634 | /// // use p... 635 | /// mi::mi_free(p); 636 | /// 637 | /// // Clean up the heap. Note that pointers allocated from `h` 638 | /// // are *not* invalided by `mi_heap_delete`. You would have 639 | /// // to use (the very dangerous) `mi_heap_destroy` for that 640 | /// // behavior 641 | /// mi::mi_heap_delete(h); 642 | /// } 643 | /// ``` 644 | pub enum mi_heap_t {} 645 | 646 | /// An area of heap space contains blocks of a single size. 647 | /// 648 | /// The bytes in freed blocks are `committed - used`. 649 | #[repr(C)] 650 | #[derive(Debug, Clone, Copy)] 651 | pub struct mi_heap_area_t { 652 | /// Start of the area containing heap blocks. 653 | pub blocks: *mut c_void, 654 | /// Bytes reserved for this area. 655 | pub reserved: usize, 656 | /// Current committed bytes of this area. 657 | pub committed: usize, 658 | /// Bytes in use by allocated blocks. 659 | pub used: usize, 660 | /// Size in bytes of one block. 661 | pub block_size: usize, 662 | /// Size in bytes of a full block including padding and metadata. 663 | pub full_block_size: usize, 664 | /// Heap tag associated with this area 665 | pub heap_tag: i32, 666 | } 667 | 668 | /// Visitor function passed to [`mi_heap_visit_blocks`] 669 | /// 670 | /// Should return `true` to continue, and `false` to stop visiting (i.e. break) 671 | /// 672 | /// This function is always first called for every `area` with `block` as a null 673 | /// pointer. If `visit_all_blocks` was `true`, the function is then called for 674 | /// every allocated block in that area. 675 | pub type mi_block_visit_fun = Option< 676 | unsafe extern "C" fn( 677 | heap: *const mi_heap_t, 678 | area: *const mi_heap_area_t, 679 | block: *mut c_void, 680 | block_size: usize, 681 | arg: *mut c_void, 682 | ) -> bool, 683 | >; 684 | 685 | extern "C" { 686 | /// Create a new heap that can be used for allocation. 687 | pub fn mi_heap_new() -> *mut mi_heap_t; 688 | 689 | /// Delete a previously allocated heap. 690 | /// 691 | /// This will release resources and migrate any still allocated blocks in 692 | /// this heap (efficienty) to the default heap. 693 | /// 694 | /// If `heap` is the default heap, the default heap is set to the backing 695 | /// heap. 696 | pub fn mi_heap_delete(heap: *mut mi_heap_t); 697 | 698 | /// Destroy a heap, freeing all its still allocated blocks. 699 | /// 700 | /// Use with care as this will free all blocks still allocated in the heap. 701 | /// However, this can be a very efficient way to free all heap memory in one 702 | /// go. 703 | /// 704 | /// If `heap` is the default heap, the default heap is set to the backing 705 | /// heap. 706 | pub fn mi_heap_destroy(heap: *mut mi_heap_t); 707 | 708 | /// Set the default heap to use for [`mi_malloc`](crate::mi_malloc) et al. 709 | /// 710 | /// Returns the previous default heap. 711 | pub fn mi_heap_set_default(heap: *mut mi_heap_t) -> *mut mi_heap_t; 712 | 713 | /// Get the default heap that is used for [`mi_malloc`](crate::mi_malloc) et al. 714 | pub fn mi_heap_get_default() -> *mut mi_heap_t; 715 | 716 | /// Get the backing heap. 717 | /// 718 | /// The _backing_ heap is the initial default heap for a thread and always 719 | /// available for allocations. It cannot be destroyed or deleted except by 720 | /// exiting the thread. 721 | pub fn mi_heap_get_backing() -> *mut mi_heap_t; 722 | 723 | /// Release outstanding resources in a specific heap. 724 | /// 725 | /// See also [`mi_collect`]. 726 | pub fn mi_heap_collect(heap: *mut mi_heap_t, force: bool); 727 | 728 | /// Equivalent to [`mi_malloc`](crate::mi_malloc), but allocates out of the 729 | /// specific heap instead of the default. 730 | pub fn mi_heap_malloc(heap: *mut mi_heap_t, size: usize) -> *mut c_void; 731 | 732 | /// Equivalent to [`mi_zalloc`](crate::mi_zalloc), but allocates out of the 733 | /// specific heap instead of the default. 734 | pub fn mi_heap_zalloc(heap: *mut mi_heap_t, size: usize) -> *mut c_void; 735 | 736 | /// Equivalent to [`mi_calloc`], but allocates out of the specific heap 737 | /// instead of the default. 738 | pub fn mi_heap_calloc(heap: *mut mi_heap_t, count: usize, size: usize) -> *mut c_void; 739 | 740 | /// Equivalent to [`mi_mallocn`], but allocates out of the specific heap 741 | /// instead of the default. 742 | pub fn mi_heap_mallocn(heap: *mut mi_heap_t, count: usize, size: usize) -> *mut c_void; 743 | 744 | /// Equivalent to [`mi_malloc_small`], but allocates out of the specific 745 | /// heap instead of the default. 746 | /// 747 | /// `size` must be smaller or equal to [`MI_SMALL_SIZE_MAX`]. 748 | pub fn mi_heap_malloc_small(heap: *mut mi_heap_t, size: usize) -> *mut c_void; 749 | 750 | /// Equivalent to [`mi_realloc`](crate::mi_realloc), but allocates out of 751 | /// the specific heap instead of the default. 752 | pub fn mi_heap_realloc(heap: *mut mi_heap_t, p: *mut c_void, newsize: usize) -> *mut c_void; 753 | 754 | /// Equivalent to [`mi_reallocn`], but allocates out of the specific heap 755 | /// instead of the default. 756 | pub fn mi_heap_reallocn( 757 | heap: *mut mi_heap_t, 758 | p: *mut c_void, 759 | count: usize, 760 | size: usize, 761 | ) -> *mut c_void; 762 | 763 | /// Equivalent to [`mi_reallocf`], but allocates out of the specific heap 764 | /// instead of the default. 765 | pub fn mi_heap_reallocf(heap: *mut mi_heap_t, p: *mut c_void, newsize: usize) -> *mut c_void; 766 | 767 | /// Equivalent to [`mi_strdup`], but allocates out of the specific heap 768 | /// instead of the default. 769 | pub fn mi_heap_strdup(heap: *mut mi_heap_t, s: *const c_char) -> *mut c_char; 770 | 771 | /// Equivalent to [`mi_strndup`], but allocates out of the specific heap 772 | /// instead of the default. 773 | pub fn mi_heap_strndup(heap: *mut mi_heap_t, s: *const c_char, n: usize) -> *mut c_char; 774 | 775 | /// Equivalent to [`mi_realpath`], but allocates out of the specific heap 776 | /// instead of the default. 777 | pub fn mi_heap_realpath( 778 | heap: *mut mi_heap_t, 779 | fname: *const c_char, 780 | resolved_name: *mut c_char, 781 | ) -> *mut c_char; 782 | 783 | /// Equivalent to [`mi_malloc_aligned`](crate::mi_malloc_aligned), but 784 | /// allocates out of the specific heap instead of the default. 785 | pub fn mi_heap_malloc_aligned( 786 | heap: *mut mi_heap_t, 787 | size: usize, 788 | alignment: usize, 789 | ) -> *mut c_void; 790 | 791 | /// Equivalent to [`mi_malloc_aligned_at`], but allocates out of the 792 | /// specific heap instead of the default. 793 | pub fn mi_heap_malloc_aligned_at( 794 | heap: *mut mi_heap_t, 795 | size: usize, 796 | alignment: usize, 797 | offset: usize, 798 | ) -> *mut c_void; 799 | 800 | /// Equivalent to [`mi_zalloc_aligned`](crate::mi_zalloc_aligned), but 801 | /// allocates out of the specific heap instead of the default. 802 | pub fn mi_heap_zalloc_aligned( 803 | heap: *mut mi_heap_t, 804 | size: usize, 805 | alignment: usize, 806 | ) -> *mut c_void; 807 | 808 | /// Equivalent to [`mi_zalloc_aligned_at`], but allocates out of the 809 | /// specific heap instead of the default. 810 | pub fn mi_heap_zalloc_aligned_at( 811 | heap: *mut mi_heap_t, 812 | size: usize, 813 | alignment: usize, 814 | offset: usize, 815 | ) -> *mut c_void; 816 | 817 | /// Equivalent to [`mi_calloc_aligned`], but allocates out of the specific 818 | /// heap instead of the default. 819 | pub fn mi_heap_calloc_aligned( 820 | heap: *mut mi_heap_t, 821 | count: usize, 822 | size: usize, 823 | alignment: usize, 824 | ) -> *mut c_void; 825 | 826 | /// Equivalent to [`mi_calloc_aligned_at`], but allocates out of the 827 | /// specific heap instead of the default. 828 | pub fn mi_heap_calloc_aligned_at( 829 | heap: *mut mi_heap_t, 830 | count: usize, 831 | size: usize, 832 | alignment: usize, 833 | offset: usize, 834 | ) -> *mut c_void; 835 | 836 | /// Equivalent to [`mi_realloc_aligned`](crate::mi_realloc_aligned), but allocates out of the specific 837 | /// heap instead of the default. 838 | pub fn mi_heap_realloc_aligned( 839 | heap: *mut mi_heap_t, 840 | p: *mut c_void, 841 | newsize: usize, 842 | alignment: usize, 843 | ) -> *mut c_void; 844 | 845 | /// Equivalent to [`mi_realloc_aligned_at`], but allocates out of the 846 | /// specific heap instead of the default. 847 | pub fn mi_heap_realloc_aligned_at( 848 | heap: *mut mi_heap_t, 849 | p: *mut c_void, 850 | newsize: usize, 851 | alignment: usize, 852 | offset: usize, 853 | ) -> *mut c_void; 854 | 855 | /// Equivalent to [`mi_rezalloc`], but allocates out of the specific heap 856 | /// instead of the default. 857 | pub fn mi_heap_rezalloc(heap: *mut mi_heap_t, p: *mut c_void, newsize: usize) -> *mut c_void; 858 | 859 | /// Equivalent to [`mi_recalloc`], but allocates out of the specific heap 860 | /// instead of the default. 861 | pub fn mi_heap_recalloc( 862 | heap: *mut mi_heap_t, 863 | p: *mut c_void, 864 | newcount: usize, 865 | size: usize, 866 | ) -> *mut c_void; 867 | 868 | /// Equivalent to [`mi_rezalloc_aligned`], but allocates out of the specific 869 | /// heap instead of the default. 870 | pub fn mi_heap_rezalloc_aligned( 871 | heap: *mut mi_heap_t, 872 | p: *mut c_void, 873 | newsize: usize, 874 | alignment: usize, 875 | ) -> *mut c_void; 876 | 877 | /// Equivalent to [`mi_rezalloc_aligned_at`], but allocates out of the 878 | /// specific heap instead of the default. 879 | pub fn mi_heap_rezalloc_aligned_at( 880 | heap: *mut mi_heap_t, 881 | p: *mut c_void, 882 | newsize: usize, 883 | alignment: usize, 884 | offset: usize, 885 | ) -> *mut c_void; 886 | 887 | /// Equivalent to [`mi_recalloc_aligned`], but allocates out of the 888 | /// specific heap instead of the default. 889 | pub fn mi_heap_recalloc_aligned( 890 | heap: *mut mi_heap_t, 891 | p: *mut c_void, 892 | newcount: usize, 893 | size: usize, 894 | alignment: usize, 895 | ) -> *mut c_void; 896 | 897 | /// Equivalent to [`mi_recalloc_aligned_at`], but allocates out of the 898 | /// specific heap instead of the default. 899 | pub fn mi_heap_recalloc_aligned_at( 900 | heap: *mut mi_heap_t, 901 | p: *mut c_void, 902 | newcount: usize, 903 | size: usize, 904 | alignment: usize, 905 | offset: usize, 906 | ) -> *mut c_void; 907 | 908 | /// Does a heap contain a pointer to a previously allocated block? 909 | /// 910 | /// `p` must be a pointer to a previously allocated block (in any heap) -- it cannot be some 911 | /// random pointer! 912 | /// 913 | /// Returns `true` if the block pointed to by `p` is in the `heap`. 914 | /// 915 | /// See [`mi_heap_check_owned`]. 916 | pub fn mi_heap_contains_block(heap: *mut mi_heap_t, p: *const c_void) -> bool; 917 | 918 | /// Check safely if any pointer is part of a heap. 919 | /// 920 | /// `p` may be any pointer -- not required to be previously allocated by the 921 | /// given heap or any other mimalloc heap. Returns `true` if `p` points to a 922 | /// block in the given heap, false otherwise. 923 | /// 924 | /// Note: expensive function, linear in the pages in the heap. 925 | /// 926 | /// See [`mi_heap_contains_block`], [`mi_heap_get_default`], and 927 | /// [`mi_is_in_heap_region`] 928 | pub fn mi_heap_check_owned(heap: *mut mi_heap_t, p: *const c_void) -> bool; 929 | 930 | /// Check safely if any pointer is part of the default heap of this thread. 931 | /// 932 | /// `p` may be any pointer -- not required to be previously allocated by the 933 | /// default heap for this thread, or any other mimalloc heap. Returns `true` 934 | /// if `p` points to a block in the default heap, false otherwise. 935 | /// 936 | /// Note: expensive function, linear in the pages in the heap. 937 | /// 938 | /// See [`mi_heap_contains_block`], [`mi_heap_get_default`] 939 | pub fn mi_check_owned(p: *const c_void) -> bool; 940 | 941 | /// Visit all areas and blocks in `heap`. 942 | /// 943 | /// If `visit_all_blocks` is false, the `visitor` is only called once for 944 | /// every heap area. If it's true, the `visitor` is also called for every 945 | /// allocated block inside every area (with `!block.is_null()`). Return 946 | /// `false` from the `visitor` to return early. 947 | /// 948 | /// `arg` is an extra argument passed into the `visitor`. 949 | /// 950 | /// Returns `true` if all areas and blocks were visited. 951 | /// 952 | /// Passing a `None` visitor is allowed, and is a no-op. 953 | pub fn mi_heap_visit_blocks( 954 | heap: *const mi_heap_t, 955 | visit_all_blocks: bool, 956 | visitor: mi_block_visit_fun, 957 | arg: *mut c_void, 958 | ) -> bool; 959 | 960 | #[cfg(feature = "arena")] 961 | /// Create a heap that only allocates in the specified arena 962 | pub fn mi_heap_new_in_arena(arena_id: mi_arena_id_t) -> *mut mi_heap_t; 963 | 964 | #[cfg(feature = "arena")] 965 | /// Reserve OS memory for use by mimalloc. Reserved areas are used 966 | /// before allocating from the OS again. By reserving a large area upfront, 967 | /// allocation can be more efficient, and can be better managed on systems 968 | /// without `mmap`/`VirtualAlloc` (like WASM for example). 969 | /// 970 | /// - `size` The size to reserve. 971 | /// - `commit` Commit the memory upfront. 972 | /// - `allow_large` Allow large OS pages (2MiB) to be used? 973 | /// - `exclusive` Only allow allocations if specifically for this arena. 974 | /// - `arena_id` Pointer who's value will be set to the new arena_id if successful. 975 | /// 976 | /// Returns 0 if successful, and an error code otherwise (e.g. `ENOMEM`) 977 | pub fn mi_reserve_os_memory_ex( 978 | size: usize, 979 | commit: bool, 980 | allow_large: bool, 981 | exclusive: bool, 982 | arena_id: *mut mi_arena_id_t, 983 | ) -> c_int; 984 | 985 | #[cfg(feature = "arena")] 986 | /// Manage a particular memory area for use by mimalloc. 987 | /// This is just like `mi_reserve_os_memory_ex` except that the area should already be 988 | /// allocated in some manner and available for use my mimalloc. 989 | /// 990 | /// # Safety 991 | /// mimalloc will likely segfault when allocating from the arena if the arena `start` & `size` 992 | /// aren't aligned with mimalloc's `MI_SEGMENT_ALIGN` (e.g. 32MB on x86_64 machines). 993 | /// 994 | /// - `start` Start of the memory area 995 | /// - `size` The size of the memory area. Must be large than `MI_ARENA_BLOCK_SIZE` (e.g. 64MB 996 | /// on x86_64 machines). 997 | /// - `commit` Set true if the memory range is already commited. 998 | /// - `is_large` Set true if the memory range consists of large files, or if the memory should 999 | /// not be decommitted or protected (like rdma etc.). 1000 | /// - `is_zero` Set true if the memory range consists only of zeros. 1001 | /// - `numa_node` Possible associated numa node or `-1`. 1002 | /// - `exclusive` Only allow allocations if specifically for this arena. 1003 | /// - `arena_id` Pointer who's value will be set to the new arena_id if successful. 1004 | /// 1005 | /// Returns `true` if arena was successfully allocated 1006 | pub fn mi_manage_os_memory_ex( 1007 | start: *const c_void, 1008 | size: usize, 1009 | is_committed: bool, 1010 | is_large: bool, 1011 | is_zero: bool, 1012 | numa_node: c_int, 1013 | exclusive: bool, 1014 | arena_id: *mut mi_arena_id_t, 1015 | ) -> bool; 1016 | } 1017 | 1018 | #[cfg(test)] 1019 | mod tests { 1020 | use super::*; 1021 | 1022 | #[test] 1023 | fn it_calculates_usable_size() { 1024 | let ptr = unsafe { mi_malloc(32) } as *mut u8; 1025 | let usable_size = unsafe { mi_usable_size(ptr as *mut c_void) }; 1026 | assert!( 1027 | usable_size >= 32, 1028 | "usable_size should at least equal to the allocated size" 1029 | ); 1030 | } 1031 | 1032 | #[test] 1033 | fn runtime_stable_option() { 1034 | unsafe { 1035 | assert_eq!(mi_option_get(mi_option_show_errors), 0); 1036 | mi_option_set(mi_option_show_errors, 1); 1037 | assert_eq!(mi_option_get(mi_option_show_errors), 1); 1038 | 1039 | assert_eq!(mi_option_get(mi_option_show_stats), 0); 1040 | mi_option_set(mi_option_show_stats, 1); 1041 | assert_eq!(mi_option_get(mi_option_show_stats), 1); 1042 | 1043 | assert_eq!(mi_option_get(mi_option_verbose), 0); 1044 | mi_option_set(mi_option_verbose, 1); 1045 | assert_eq!(mi_option_get(mi_option_verbose), 1); 1046 | } 1047 | } 1048 | } 1049 | --------------------------------------------------------------------------------