Skip to content

Commit ff8a878

Browse files
committed
split the rust crate with a -sys crate as well, issue #1405
1 parent caaf3bc commit ff8a878

9 files changed

Lines changed: 283 additions & 219 deletions

File tree

‎contrib/rust/.gitignore‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
/target
22
/Cargo.lock
3-
/mimalloc
3+
/sys/mimalloc

‎contrib/rust/Cargo.toml‎

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,29 @@ description = "Rust bindings and global allocator for the mimalloc memory alloca
1414
repository = "https://github.com/microsoft/mimalloc"
1515
keywords = ["mimalloc", "allocator", "malloc", "heap"]
1616
categories = ["memory-management", "api-bindings", "no-std"]
17-
links = "mimalloc"
18-
# a published crate should vendor the C sources in `mimalloc/` (see the readme)
19-
include = ["/src", "/tests", "/build.rs", "/README.md", "/mimalloc/LICENSE", "/mimalloc/include", "/mimalloc/src"]
17+
include = ["/src", "/tests", "/build.rs", "/README.md"]
18+
19+
[workspace]
20+
members = ["sys"]
2021

2122
[features]
2223
default = []
23-
# security mitigations (guard pages, randomization, encoded free lists, double free detection)
24-
secure = []
25-
# also override the standard `malloc`, `free`, etc. in the final binary
26-
override = []
27-
# build mimalloc with assertions and internal invariant checks
28-
debug = []
29-
# as `debug` but only when `debug_assertions` are enabled
30-
debug_in_debug = []
31-
# use the local-dynamic TLS model (when mimalloc is part of a dynamically loaded library)
32-
local_dynamic_tls = []
33-
# use only direct TLS slots on Windows (faster but requires fewer than 64 TLS slots in total)
34-
win_direct_tls = []
35-
# disable transparent huge pages by default on Linux
36-
no_thp = []
37-
# use architecture specific optimizations (as cmake `-DMI_OPT_ARCH=ON`)
38-
opt_arch = []
24+
# build options of the C library (see `sys/Cargo.toml`)
25+
secure = ["mimalloc-sys/secure"]
26+
override = ["mimalloc-sys/override"]
27+
debug = ["mimalloc-sys/debug"]
28+
debug_in_debug = ["mimalloc-sys/debug_in_debug"]
29+
local_dynamic_tls = ["mimalloc-sys/local_dynamic_tls"]
30+
win_direct_tls = ["mimalloc-sys/win_direct_tls"]
31+
no_thp = ["mimalloc-sys/no_thp"]
32+
opt_arch = ["mimalloc-sys/opt_arch"]
3933
# implement `core::alloc::Allocator` on nightly compilers before Rust 1.100 (where it is stable)
4034
nightly = []
4135
# for compatibility with the `mimalloc` crate of mimalloc_rust (the full API is always available)
4236
extended = []
4337
arena = []
4438
nightly_allocator_api = ["nightly"]
4539

46-
[build-dependencies]
47-
cc = "1.2"
40+
[dependencies]
41+
# the exact version as the fast paths depend on `MI_SMALL_SIZE_MAX` and internal entry points
42+
mimalloc-sys = { version = "=3.6.0", path = "sys" }

‎contrib/rust/README.md‎

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
# mimalloc for Rust
22

33
Rust bindings for [mimalloc](https://github.com/microsoft/mimalloc), built directly
4-
from the mimalloc sources in this repository.
4+
from the mimalloc sources in this repository. As usual for Rust FFI bindings, these are split into two crates:
5+
6+
- `mimalloc-sys` (in `sys/`): builds and links the C library and has the raw C API.
7+
- `mimalloc`: the global allocator, heaps, and safe helpers (with the raw API re-exported as `mimalloc::sys`).
58

69
```rust
710
use mimalloc::MiMalloc;
@@ -30,7 +33,10 @@ static GLOBAL: MiMalloc = MiMalloc;
3033
| `win_direct_tls` | Use direct TLS slots on Windows |
3134
| `no_thp` | Disable transparent huge pages by default on Linux |
3235
| `opt_arch` | Architecture specific optimizations (as `-DMI_OPT_ARCH=ON`) |
33-
| `nightly` | `Allocator` support on nightly compilers before Rust 1.100 |
36+
| `nightly` | `Allocator` support on nightly compilers before Rust 1.100 (`mimalloc` only) |
37+
38+
All features except `nightly` are build options of the C library in `mimalloc-sys`
39+
(and are forwarded by `mimalloc`).
3440

3541
Architecture optimizations are also enabled automatically if the Rust target features
3642
already imply them (e.g. `-C target-cpu=native`).
@@ -48,10 +54,10 @@ For example: `MIMALLOC_DEFINES="MI_DEBUG=3 MI_SKIP_COLLECT_ON_EXIT=1" cargo buil
4854

4955
## Using the crate
5056

51-
The C sources are found in the following order:
57+
The C sources are found by `mimalloc-sys` in the following order:
5258
1. The `MIMALLOC_SRC_DIR` environment variable.
53-
2. A vendored `mimalloc/` directory next to `Cargo.toml` (with `include/`, `src/`, and `LICENSE`).
54-
3. The enclosing repository (`../..`).
59+
2. A vendored `mimalloc/` directory next to `sys/Cargo.toml` (with `include/`, `src/`, and `LICENSE`).
60+
3. The enclosing repository (`../../..` from `sys/`).
5561

5662
Within this repository, use a path or git dependency:
5763

@@ -60,8 +66,11 @@ Within this repository, use a path or git dependency:
6066
mimalloc = { path = ".../mimalloc/contrib/rust" }
6167
```
6268

63-
To publish a crate based on this one, copy `contrib/rust`, vendor the sources into
64-
`mimalloc/` (`cp -r ../../include ../../src ../../LICENSE mimalloc/`), and rename the package.
69+
To publish crates based on these, copy `contrib/rust`, vendor the sources into
70+
`sys/mimalloc/` (`cp -r ../../include ../../src ../../LICENSE sys/mimalloc/`), and rename both
71+
packages (the names `mimalloc` and `mimalloc-sys` are already taken on crates.io). The
72+
`mimalloc` crate depends on the exact version of `mimalloc-sys` as its fast paths depend
73+
on internal entry points such as `mi_wmalloc_small` and `MI_SMALL_SIZE_MAX`.
6574

6675
## Performance tips
6776

@@ -71,9 +80,9 @@ To publish a crate based on this one, copy `contrib/rust`, vendor the sources in
7180
[profile.release]
7281
lto = "thin"
7382
```
74-
- Optimize mimalloc in debug builds too:
83+
- Optimize mimalloc in debug builds too (the C library is compiled as part of `mimalloc-sys`):
7584
```toml
76-
[profile.dev.package.mimalloc]
85+
[profile.dev.package.mimalloc-sys]
7786
opt-level = 3
7887
```
7988
- Cross-language LTO can inline the allocation fast paths into Rust code: build with
@@ -83,7 +92,7 @@ To publish a crate based on this one, copy `contrib/rust`, vendor the sources in
8392
## Testing
8493

8594
```sh
86-
cargo test
95+
cargo test --workspace
8796
cargo test --release --features secure
8897
cargo test --features debug,override
8998
cargo +nightly test --features nightly

‎contrib/rust/build.rs‎

Lines changed: 0 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -5,43 +5,6 @@ terms of the MIT license. A copy of the license can be found in the file
55
"LICENSE" at the root of this distribution.
66
-----------------------------------------------------------------------------*/
77
use std::env;
8-
use std::path::PathBuf;
9-
10-
// Locate the mimalloc sources: `MIMALLOC_SRC_DIR`, a vendored `mimalloc/` directory, or this repository.
11-
fn mimalloc_dir() -> PathBuf {
12-
println!("cargo:rerun-if-env-changed=MIMALLOC_SRC_DIR");
13-
let manifest = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
14-
let dir = match env::var_os("MIMALLOC_SRC_DIR") {
15-
Some(dir) => PathBuf::from(dir),
16-
None if manifest.join("mimalloc/src/static.c").exists() => manifest.join("mimalloc"),
17-
None => manifest.join("../.."),
18-
};
19-
if !dir.join("src/static.c").exists() {
20-
panic!(
21-
"cannot find the mimalloc sources in {} (set MIMALLOC_SRC_DIR)",
22-
dir.display()
23-
);
24-
}
25-
dir
26-
}
27-
28-
fn feature(name: &str) -> bool {
29-
env::var_os(format!("CARGO_FEATURE_{}", name.to_uppercase())).is_some()
30-
}
31-
32-
// Extra defines from `MIMALLOC_DEFINES`, e.g. `MI_DEBUG=3 MI_SKIP_COLLECT_ON_EXIT=1`
33-
fn user_defines() -> Vec<(String, Option<String>)> {
34-
println!("cargo:rerun-if-env-changed=MIMALLOC_DEFINES");
35-
let defines = env::var("MIMALLOC_DEFINES").unwrap_or_default();
36-
defines
37-
.split(|c: char| c.is_whitespace() || c == ',')
38-
.filter(|d| !d.is_empty())
39-
.map(|d| match d.split_once('=') {
40-
Some((name, value)) => (name.to_string(), Some(value.to_string())),
41-
None => (d.to_string(), None),
42-
})
43-
.collect()
44-
}
458

469
// The `Allocator` trait is stable since Rust 1.100 (but not on all 1.100 nightlies)
4710
fn allocator_api_is_stable() -> bool {
@@ -63,147 +26,4 @@ fn main() {
6326
if allocator_api_is_stable() {
6427
println!("cargo:rustc-cfg=allocator_api_stable");
6528
}
66-
67-
let dir = mimalloc_dir();
68-
let include = dir.join("include");
69-
println!("cargo:rerun-if-changed={}", include.display());
70-
println!("cargo:rerun-if-changed={}", dir.join("src").display());
71-
println!("cargo:include={}", include.display());
72-
73-
let var = |name: &str| env::var(name).unwrap_or_default();
74-
let target_os = var("CARGO_CFG_TARGET_OS");
75-
let target_env = var("CARGO_CFG_TARGET_ENV");
76-
let target_arch = var("CARGO_CFG_TARGET_ARCH");
77-
let target_vendor = var("CARGO_CFG_TARGET_VENDOR");
78-
let target_features = var("CARGO_CFG_TARGET_FEATURE");
79-
let has_features = |names: &[&str]| {
80-
names
81-
.iter()
82-
.all(|f| target_features.split(',').any(|t| t == *f))
83-
};
84-
let is_apple = target_vendor == "apple";
85-
let is_windows = target_os == "windows";
86-
let is_musl = target_env == "musl";
87-
88-
let mut build = cc::Build::new();
89-
build.include(&include).file(dir.join("src/static.c"));
90-
let compiler = build.get_compiler();
91-
let is_msvc = compiler.is_like_msvc();
92-
let is_gnu_like = (compiler.is_like_gnu() || compiler.is_like_clang()) && !is_msvc;
93-
94-
// `MIMALLOC_DEFINES` take precedence over the defines set by features
95-
let user = user_defines();
96-
let is_user = |name: &str| user.iter().any(|(n, _)| n == name);
97-
let mut define = |name: &str, value: &str| {
98-
if !is_user(name) {
99-
build.define(name, value);
100-
}
101-
};
102-
103-
// build options
104-
let debug = feature("debug")
105-
|| (feature("debug_in_debug") && env::var_os("CARGO_CFG_DEBUG_ASSERTIONS").is_some());
106-
if debug {
107-
define("MI_DEBUG", "2");
108-
} else if !is_user("MI_DEBUG") {
109-
define("MI_DEBUG", "0");
110-
define("NDEBUG", "1");
111-
}
112-
if feature("secure") {
113-
define("MI_SECURE", "4");
114-
}
115-
let is_override = feature("override") && !is_windows;
116-
if is_override {
117-
define("MI_MALLOC_OVERRIDE", "1");
118-
if is_apple {
119-
define("MI_OSX_ZONE", "1");
120-
define("MI_OSX_INTERPOSE", "1");
121-
}
122-
}
123-
if feature("no_thp") && (target_os == "linux" || target_os == "android") {
124-
define("MI_DEFAULT_ALLOW_THP", "0");
125-
}
126-
if is_musl {
127-
define("MI_LIBC_MUSL", "1");
128-
}
129-
if feature("win_direct_tls") && is_windows {
130-
define("MI_WIN_DIRECT_TLS", "1");
131-
}
132-
for (name, value) in &user {
133-
build.define(name, value.as_deref());
134-
}
135-
if is_override && is_gnu_like {
136-
build.flag("-fno-builtin-malloc");
137-
}
138-
139-
// compiler flags (as in CMakeLists.txt)
140-
if is_msvc {
141-
// use C++ atomics with msvc and clang-cl
142-
build
143-
.cpp(true)
144-
.flag("/TP")
145-
.flag("/Zc:__cplusplus")
146-
.std("c++17");
147-
} else if is_gnu_like {
148-
build
149-
.flag("-fvisibility=hidden")
150-
.flag("-Wno-unknown-pragmas");
151-
if compiler.is_like_clang() {
152-
build
153-
.flag("-Wno-static-in-inline")
154-
.flag_if_supported("-mno-outline");
155-
if is_apple && target_arch == "aarch64" && is_apple_clang(&compiler) {
156-
build.flag("-Xclang").flag("-fno-split-cold-code");
157-
}
158-
}
159-
if target_os != "haiku" {
160-
let tls = if feature("local_dynamic_tls") || is_musl {
161-
"local-dynamic"
162-
} else {
163-
"initial-exec"
164-
};
165-
build.flag(format!("-ftls-model={tls}"));
166-
}
167-
}
168-
169-
// architecture specific optimizations: enabled with `opt_arch`, or if the Rust target already implies them
170-
let opt_arch = feature("opt_arch");
171-
match target_arch.as_str() {
172-
"x86_64" if opt_arch || has_features(&["avx2", "bmi1", "bmi2", "lzcnt"]) => {
173-
if is_msvc {
174-
build.flag("/arch:AVX2");
175-
} else if is_gnu_like {
176-
build.flag("-march=haswell").flag("-mavx2");
177-
}
178-
}
179-
"aarch64" if is_apple && (opt_arch || has_features(&["lse", "rcpc"])) => {
180-
if is_gnu_like {
181-
build.flag("-march=armv8.3-a");
182-
}
183-
}
184-
"aarch64" if opt_arch || has_features(&["lse"]) => {
185-
if is_msvc {
186-
build.flag("/arch:armv8.1");
187-
} else if is_gnu_like {
188-
build.flag("-march=armv8.1-a");
189-
}
190-
}
191-
_ => {}
192-
}
193-
194-
build.compile("mimalloc");
195-
196-
// system libraries
197-
if is_windows {
198-
for lib in ["psapi", "shell32", "user32", "advapi32", "bcrypt"] {
199-
println!("cargo:rustc-link-lib={lib}");
200-
}
201-
} else if target_os == "linux" && (target_arch == "arm" || target_arch.starts_with("riscv32")) {
202-
println!("cargo:rustc-link-lib=atomic");
203-
}
204-
}
205-
206-
fn is_apple_clang(compiler: &cc::Tool) -> bool {
207-
let out = compiler.to_command().arg("--version").output();
208-
out.is_ok_and(|out| String::from_utf8_lossy(&out.stdout).contains("Apple clang"))
20929
}

‎contrib/rust/src/lib.rs‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ terms of the MIT license. A copy of the license can be found in the file
2525
#[cfg(any(feature = "nightly", allocator_api_stable))]
2626
mod allocator;
2727
mod heap;
28-
pub mod sys;
28+
pub use mimalloc_sys as sys;
2929

3030
pub use heap::Heap;
3131

@@ -226,8 +226,3 @@ impl core::fmt::Debug for MiString {
226226
core::fmt::Debug::fmt(self.as_c_str(), f)
227227
}
228228
}
229-
230-
// keep the malloc overrides when linking statically
231-
#[cfg(feature = "override")]
232-
#[used]
233-
static OVERRIDE: extern "C" fn(usize) -> *mut core::ffi::c_void = sys::mi_malloc;

‎contrib/rust/sys/Cargo.toml‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# ----------------------------------------------------------------------------
2+
# Copyright (c) 2018-2026, Microsoft Research, Daan Leijen
3+
# This is free software; you can redistribute it and/or modify it under the
4+
# terms of the MIT license. A copy of the license can be found in the file
5+
# "LICENSE" at the root of this distribution.
6+
# -----------------------------------------------------------------------------
7+
[package]
8+
name = "mimalloc-sys"
9+
version = "3.6.0"
10+
edition = "2024"
11+
rust-version = "1.85"
12+
license = "MIT"
13+
description = "Raw bindings to the mimalloc memory allocator (builds and links the C library)"
14+
repository = "https://github.com/microsoft/mimalloc"
15+
keywords = ["mimalloc", "allocator", "malloc", "ffi"]
16+
categories = ["memory-management", "external-ffi-bindings", "no-std"]
17+
links = "mimalloc"
18+
# a published crate should vendor the C sources in `mimalloc/` (see the readme)
19+
include = ["/src", "/build.rs", "/README.md", "/mimalloc/LICENSE", "/mimalloc/include", "/mimalloc/src"]
20+
21+
[features]
22+
default = []
23+
# security mitigations (guard pages, randomization, encoded free lists, double free detection)
24+
secure = []
25+
# also override the standard `malloc`, `free`, etc. in the final binary
26+
override = []
27+
# build mimalloc with assertions and internal invariant checks
28+
debug = []
29+
# as `debug` but only when `debug_assertions` are enabled
30+
debug_in_debug = []
31+
# use the local-dynamic TLS model (when mimalloc is part of a dynamically loaded library)
32+
local_dynamic_tls = []
33+
# use only direct TLS slots on Windows (faster but requires fewer than 64 TLS slots in total)
34+
win_direct_tls = []
35+
# disable transparent huge pages by default on Linux
36+
no_thp = []
37+
# use architecture specific optimizations (as cmake `-DMI_OPT_ARCH=ON`)
38+
opt_arch = []
39+
40+
[build-dependencies]
41+
cc = "1.2"

‎contrib/rust/sys/README.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# mimalloc-sys
2+
3+
Raw bindings to the [mimalloc](https://github.com/microsoft/mimalloc) C API (`mimalloc.h`).
4+
This crate builds and links the C library; use the `mimalloc` crate (in the parent
5+
directory) for the global allocator, heaps, and safe helpers.
6+
7+
See the [`mimalloc` readme](../README.md) for the features, build options, and how the
8+
C sources are located.

0 commit comments

Comments
 (0)