├── .cirrus.yml ├── .github ├── dependabot.yml └── workflows │ └── rust.yml ├── .gitignore ├── .gitmodules ├── .travis.yml ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── snmalloc-sys ├── Cargo.toml ├── build.rs └── src │ └── lib.rs └── src └── lib.rs /.cirrus.yml: -------------------------------------------------------------------------------- 1 | freebsd_instance: 2 | image_family: freebsd-15-0-snap 3 | task: 4 | name: cargo test (stable) 5 | env: 6 | HOME: /tmp # cargo needs it 7 | install_script: | 8 | pkg install -y rust 9 | pkg install -y cmake 10 | pkg install -y git 11 | build_script: | 12 | git submodule update --init 13 | cargo build --all 14 | test_script: cargo test --all --all-targets 15 | 16 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | - package-ecosystem: gitsubmodule 9 | directory: "/" 10 | schedule: 11 | interval: daily 12 | open-pull-requests-limit: 10 13 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | matrix: 18 | os: [windows-latest, macos-14, macos-15, ubuntu-latest] 19 | rust: [stable, nightly] 20 | steps: 21 | - uses: actions-rs/toolchain@v1 22 | with: 23 | toolchain: ${{ matrix.rust }} 24 | - name: Checkout 25 | uses: actions/checkout@v2 26 | with: 27 | submodules: recursive 28 | - name: update dependency 29 | run: | 30 | if bash -c 'uname -s | grep 'Linux' >/dev/null'; then 31 | sudo apt-get update -y && sudo apt-get --reinstall install -y libc6-dev 32 | fi 33 | shell: bash 34 | - name: Build 35 | run: cargo build --verbose 36 | - name: Run tests 37 | run: cargo test --all 38 | - name: Run tests debug 39 | run: cargo test --all --features debug 40 | - name: Run tests check 41 | run: cargo test --all --features check 42 | - name: Run tests build_cc 43 | run: cargo test --all --features "build_cc usecxx17" 44 | - name: Run tests native-cpu 45 | run: cargo test --all --features native-cpu 46 | - name: Run tests local_dynamic_tls 47 | run: cargo test --all --features local_dynamic_tls 48 | - name: Run tests lto 49 | run: cargo test --all --features lto 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | .idea 4 | 5 | # vscode dirs 6 | .vscode/ 7 | .vs/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "new"] 2 | path = snmalloc-sys/snmalloc 3 | url = https://github.com/microsoft/snmalloc 4 | branch = main 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | matrix: 2 | include: 3 | - os: windows 4 | language: rust 5 | env: 6 | - TOOLCHAIN=MinGW 7 | rust: 8 | - nightly 9 | 10 | - os: windows 11 | language: rust 12 | rust: 13 | - stable 14 | before_script: 15 | - choco install visualstudio2019community 16 | - choco install visualstudio2019-workload-nativedesktop 17 | - export VS160COMNTOOLS="/c/Program Files (x86)/Microsoft Visual Studio/2019/Community/Common7/Tools" 18 | - rustup toolchain install stable-x86_64-pc-windows-msvc 19 | - rustup default stable-msvc 20 | 21 | - os: windows 22 | language: rust 23 | rust: 24 | - nightly 25 | before_script: 26 | - choco install visualstudio2019community 27 | - choco install visualstudio2019-workload-nativedesktop 28 | - export VS160COMNTOOLS="/c/Program Files (x86)/Microsoft Visual Studio/2019/Community/Common7/Tools" 29 | - rustup toolchain install nightly-x86_64-pc-windows-msvc 30 | - rustup default nightly-msvc 31 | 32 | - os: linux 33 | dist: bionic 34 | language: rust 35 | rust: 36 | - stable 37 | addons: 38 | apt: 39 | sources: 40 | - ubuntu-toolchain-r-test 41 | packages: 42 | - g++-9 43 | env: 44 | - CC=gcc-9 45 | - CXX=g++-9 46 | 47 | - os: linux 48 | dist: bionic 49 | language: rust 50 | rust: 51 | - nightly 52 | addons: 53 | apt: 54 | sources: 55 | - ubuntu-toolchain-r-test 56 | packages: 57 | - gcc-10 58 | - g++-10 59 | env: 60 | - CC=gcc-10 61 | - CXX=g++-10 62 | 63 | - os: linux 64 | dist: bionic 65 | arch: arm64 66 | language: rust 67 | rust: 68 | - stable 69 | 70 | - os: linux 71 | dist: bionic 72 | arch: arm64 73 | language: rust 74 | rust: 75 | - nightly 76 | 77 | - os: osx 78 | osx_image: xcode12 79 | language: rust 80 | rust: 81 | - stable 82 | 83 | - os: osx 84 | osx_image: xcode12 85 | language: rust 86 | rust: 87 | - nightly 88 | allow_failures: 89 | env: TOOLCHAIN=MinGW 90 | 91 | cache: 92 | directories: 93 | - $HOME/AppData/Local/Temp/chocolatey 94 | 95 | script: 96 | - cargo test --all 97 | - cargo test --all --features 1mib 98 | - cargo test --all --features build_cc 99 | - cargo test --all --features debug 100 | - cargo test --all --features cache-friendly 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Changelog 2 | 3 | ### 0.3.8 4 | 5 | - Tracking upstream to match version 0.7.1 6 | - Recommended to upgrade from 0.3.7 to get an important bug fix. 7 | 8 | ### 0.3.7 9 | 10 | - Tracking upstream to match version 0.7 11 | 12 | ### 0.3.4 13 | - Tracking upstream to match version 0.6.2 14 | 15 | ### 0.3.3 16 | - Tracking upstream to fix Linux PAL typo. 17 | 18 | ### 0.3.2 19 | - Tracking upstream to enable old Linux variants. 20 | 21 | ### 0.3.1 22 | - Fixes `build_cc` feature (broken in 0.3.0 release). 23 | - Fixes `native-cpu` feature (broken in 0.3.0 release). 24 | 25 | ### 0.3.0 26 | - Release to follow upstream 0.6.0 27 | - **upstream** Major redesign of the code to improve performance and 28 | enable a mode that provides strong checks against corruption. 29 | 30 | ### 0.3.0-beta.1 31 | 32 | - Beta release to support snmalloc 2 33 | 34 | ### 0.2.28 35 | - Deprecation of `cache-friendly` 36 | - Use exposed `alloc_zeroed` from `snmalloc` 37 | - **upstream** changes of remote communication, corruption detection and compilation flag detection. 38 | 39 | ### 0.2.27 40 | 41 | - Reduction of libc dependency 42 | - **upstream** Windows 7 and windows 8 compatibility added 43 | - **upstream** Option to use C++20 standards if available 44 | - **upstream** Preparations of cherification (heavy refactors of the structure) 45 | - **upstream** Cold routine annotations 46 | 47 | ### 0.2.26 48 | 49 | - **upstream** Building adjustment 50 | - option of cc crate as build feature, only c compiler needed, no cmake required 51 | - Addition of dynamic local TLS option 52 | 53 | ### 0.2.25 54 | 55 | - **upstream** Apple M1 support 56 | - **upstream** Building adjust 57 | - non-allocation tracking functions 58 | 59 | ### 0.2.24 60 | 61 | - **upstream** update to use a more efficient power of 2 check 62 | - fix msvc support w/ crt-static 63 | 64 | ### 0.2.23 65 | 66 | - **upstream** fix external pagemap usage 67 | 68 | ### 0.2.22 69 | 70 | - **upstream** avoid amplification when routing 71 | - **upstream** remotely store sizeclass 72 | - **upstream** limit flat pagemap size 73 | - **upstream** limit medium slab header 74 | - **upstream** solaris support fix 75 | 76 | ### 0.2.21 77 | 78 | - **upstream** bug fix for using failing to initialise meta-data 79 | 80 | ### 0.2.20 81 | 82 | - **upstream** pass through Haiku build fix. 83 | - **upstream** fix typo in macro definition for 16MiB shared library shim. 84 | - **upstream** DragonFly support (userland). 85 | - **upstream** natural alignment for USE_MALLOC 86 | - **upstream** fix bug in pagemap when index has many level 87 | - **upstream** add constexpr annotation to align_up/down. 88 | 89 | ### 0.2.19 90 | 91 | - **upstream** stats 92 | - **upstream** PAL updates and concepts 93 | - **upstream** ddd constexpr annotation to align_up/down 94 | - change macOS CI to follow xcode 12 95 | 96 | ### 0.2.18 97 | 98 | - add msvc flag /EHsc to fix warning C4530 99 | 100 | ### 0.2.17 101 | 102 | - **upstream** add backoff for large reservation 103 | - **upstream** default chunk configuration to 1mib 104 | - add new feature flags 105 | 106 | ### 0.2.16 107 | 108 | - **upstream** New implementation of address space reservation leading to 109 | - better integration with transparent huge pages; and 110 | - lower address space requirements and fragmentation. 111 | - Notice MinGW broken state 112 | 113 | ### 0.2.15 114 | 115 | - **upstream** fix VS2019 build 116 | - **upstream** fix wrong realloc behavior and performance issue 117 | 118 | ### 0.2.14 119 | 120 | - **upstream** refactor ptr representation. 121 | - **upstream** improve for more targets and architectures. 122 | - seperate native CPU feature 123 | 124 | ### 0.2.13 125 | 126 | - **upstream** large realloc fix and minor updates 127 | 128 | ### 0.2.12 129 | 130 | - improve mingw support 131 | 132 | ### 0.2.11 133 | 134 | - add android support 135 | - **upstream** support x86 136 | - **upstream** support android 137 | - **upstream** fix callback 138 | 139 | ### 0.2.10 140 | 141 | - follow upstream 0.4.0 142 | - **upstream** defense TLS teardown 143 | - **upstream** adjust GCC warning 144 | - **upstream** other release optimizations 145 | 146 | ### 0.2.9 147 | 148 | - **upstream** fix OpenEnclave 149 | - **upstream** adjust remote batch size (performance improved dramatically, see [benchmark](https://github.com/microsoft/snmalloc/pull/158#issuecomment-605816017) 150 | - **upstream** improve slow path performance for allocation 151 | 152 | ### 0.2.8 153 | 154 | - More CI (**ARM64 on QEMU**) 155 | - **upstream** ARM(32/64) support 156 | - **upstream** x86-SGX support 157 | 158 | ### 0.2.7 159 | 160 | - partially fixed `mingw` 161 | - **upstream** remote dealloc refactor (higher performance) 162 | - **upstream** remove extra assertions 163 | 164 | ### 0.2.6 165 | 166 | - fix `macos`/`freebsd ` support 167 | - add more ci tests 168 | - mark the `mingw` problem 169 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "snmalloc-rs" 3 | version = "0.3.8" 4 | authors = ["schrodingerzhu "] 5 | edition = "2021" 6 | license = "MIT" 7 | description = "rust bindings of snmalloc." 8 | keywords = ["snmalloc", "allocator"] 9 | categories = ["memory-management", "api-bindings"] 10 | homepage = "https://github.com/microsoft/snmalloc" 11 | repository = "https://github.com/SchrodingerZhu/snmalloc-rs" 12 | readme = "README.md" 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [workspace] 16 | members = ["snmalloc-sys"] 17 | 18 | [dependencies] 19 | snmalloc-sys = { version = "0.3.8", path = "snmalloc-sys", default-features = false } 20 | 21 | [features] 22 | default = ["snmalloc-sys/build_cmake", "snmalloc-sys/usewait-on-address"] 23 | build_cc = ["snmalloc-sys/build_cc"] 24 | qemu = ["snmalloc-sys/qemu"] 25 | debug = ["snmalloc-sys/debug"] 26 | android-lld = ["snmalloc-sys/android-lld"] 27 | native-cpu = ["snmalloc-sys/native-cpu"] 28 | local_dynamic_tls = ["snmalloc-sys/local_dynamic_tls"] 29 | win8compat = ["snmalloc-sys/win8compat"] 30 | usecxx17 = ["snmalloc-sys/usecxx17"] 31 | check = ["snmalloc-sys/check"] 32 | lto = ["snmalloc-sys/lto"] 33 | notls = ["snmalloc-sys/notls"] 34 | stats = ["snmalloc-sys/stats"] 35 | usewait-on-address = ["snmalloc-sys/usewait-on-address"] 36 | libc-api = ["snmalloc-sys/libc-api"] 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 SchrodingerZhu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # snmalloc-rs 2 | 3 | **Notice: MinGW Build is broken and may not be fixed in a near future. 4 | See [this PR](https://github.com/microsoft/snmalloc/pull/217) in the upstream.** 5 | 6 | MSVC/MinGW/Linux/MacOS: [![Actions Status](https://github.com/schrodingerzhu/snmalloc-rs/workflows/Rust/badge.svg)](https://github.com/schrodingerzhu/snmalloc-rs/actions) 7 | 8 | FreeBSD: [![Build Status](https://api.cirrus-ci.com/github/SchrodingerZhu/snmalloc-rs.svg)](https://cirrus-ci.com/github/SchrodingerZhu/snmalloc-rs) 9 | 10 | `snmalloc-rs` provides a wrapper for [`microsoft/snmalloc`](https://github.com/microsoft/snmalloc) to make it usable as 11 | a global allocator for rust. snmalloc is a research allocator. Its key design features are: 12 | 13 | - Memory that is freed by the same thread that allocated it does not require any synchronising operations. 14 | - Freeing memory in a different thread to initially allocated it, does not take any locks and instead uses a novel 15 | message passing scheme to return the memory to the original allocator, where it is recycled. 16 | - The allocator uses large ranges of pages to reduce the amount of meta-data required. 17 | 18 | Some old benchmark results are available in 19 | the [`snmalloc` paper](https://github.com/microsoft/snmalloc/blob/master/snmalloc.pdf). Some recent benchmark results 20 | are listed at 21 | [bench_suite](https://github.com/SchrodingerZhu/bench_suite). There are three features defined in this crate: 22 | 23 | - `debug`: Enable the `Debug` mode in `snmalloc`. 24 | - ~~`1mib`: Use the `1mib` chunk configuration. From `0.2.17`, this is set as a default feature~~ (removed since 0.3.0) 25 | - ~~`16mib`: Use the `16mib` chunk configuration.~~ (removed since 0.3.0) 26 | - ~~`cache-friendly`: Make the allocator more cache friendly (setting `CACHE_FRIENDLY_OFFSET` to `64` in building the 27 | library).~~ (removed since 0.3.0) 28 | - `native-cpu`: Optimize `snmalloc` for the native CPU of the host machine. (this is not a default behavior 29 | since `0.2.14`) 30 | - `qemu`: Workaround `madvise` problem of QEMU environment 31 | - ~~`stats`: Enable statistics~~ (removed since 0.3.0) 32 | - `local_dynamic_tls`: Workaround cannot allocate memory in static tls block 33 | - `build_cc`: Use of cc crate instead of cmake (cmake still default) as builder (more platform agnostic) 34 | - ~~`usecxx20`: Enable C++20 standard if available~~ (removed since 0.3.0) 35 | - `usecxx17`: Use C++17 standard 36 | - `check`: Enable extra checks to improve security, see upstream [security docs](https://github.com/microsoft/snmalloc/tree/main/docs/security). 37 | Note that the `memcpy` protection is not enabled in Rust. 38 | - `win8compat`: Improve compatibility for old Windows platforms (removing usages of `VirtualAlloc2` and other new APIs) 39 | - `lto`: Links with InterProceduralOptimization/LinkTimeOptimization 40 | - `notls`: Enables to be loaded dynamically, thus disable tls. 41 | - `stats`: Enables allocation statistics. 42 | - `libc-api`: Enables libc API backed by snmalloc. 43 | 44 | **To get the crates compiled, you need to choose either `1mib` or `16mib` to determine the chunk configuration** 45 | 46 | To use `snmalloc-rs` add it as a dependency: 47 | 48 | ```toml 49 | # Cargo.toml 50 | [dependencies] 51 | snmalloc-rs = "0.3.8" 52 | ``` 53 | 54 | To set `SnMalloc` as the global allocator add this to your project: 55 | 56 | ```rust 57 | #[global_allocator] 58 | static ALLOC: snmalloc_rs::SnMalloc = snmalloc_rs::SnMalloc; 59 | ``` 60 | 61 | ## For MinGW Users 62 | 63 | `mingw` version is only tested on nightly branch with MSYS environment. We are using dynamic linking method. Hence, 64 | please make sure the following libs are in your `PATH`: 65 | 66 | - `winpthread` 67 | - `atomic` 68 | - `stdc++` 69 | - `gcc_s` 70 | 71 | **Notice:** since version `0.2.12`, we no longer require you to provide additional environment variables for `mingw` 72 | target. 73 | 74 | ## For Android Cross-Compilation 75 | 76 | - `ANDROID_NDK` must be provided as an environment variable 77 | - `ANDROID_PLATFORM` can be passed as an optional environment variable 78 | - `ANDROID_ABI` used by CMake is detected automatically 79 | - feature `android-lld` can be used to set the linker of `snmalloc` to `lld` 80 | - ~~feature `android-shared-std` can be used to set the STL library of `snmalloc` to `c++_shared` (it uses `c++_static` by 81 | default)~~ (`libstdc++` is no longer a dependency) 82 | 83 | ## Changelog 84 | 85 | ### 0.3.8 86 | 87 | - Tracking upstream to match version 0.7.1 88 | - Recommended to upgrade from 0.3.7 to get an important bug fix. 89 | 90 | ### 0.3.7 91 | 92 | - Tracking upstream to match version 0.7 93 | 94 | ### 0.3.4 95 | - Tracking upstream to version 0.6.2. 96 | 97 | ### 0.3.3 98 | - Tracking upstream to fix Linux PAL typo. 99 | 100 | ### 0.3.2 101 | 102 | - Tracking upstream to enable old Linux variants. 103 | 104 | ### 0.3.1 105 | 106 | - Fixes `build_cc` feature (broken in 0.3.0 release). 107 | - Fixes `native-cpu` feature (broken in 0.3.0 release). 108 | 109 | ### 0.3.0 110 | 111 | - Release to support snmalloc 0.6.0. 112 | 113 | ### 0.3.0-beta.1 114 | 115 | - Beta release to support snmalloc ~~2~~ 0.6.0 116 | -------------------------------------------------------------------------------- /snmalloc-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "snmalloc-sys" 3 | version = "0.3.8" 4 | authors = ["schrodingerzhu "] 5 | edition = "2021" 6 | license = "MIT" 7 | description = "rust raw bindings of snmalloc." 8 | keywords = ["snmalloc", "allocator"] 9 | categories = ["memory-management", "api-bindings"] 10 | homepage = "https://github.com/microsoft/snmalloc" 11 | repository = "https://github.com/SchrodingerZhu/snmalloc-rs" 12 | build = "build.rs" 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [build-dependencies] 16 | cc = { version = "1.0", optional = true } 17 | cmake = { version = "0.1", optional = true } 18 | 19 | [features] 20 | default = ["build_cmake"] 21 | build_cc = ["cc"] 22 | build_cmake = ["cmake"] 23 | qemu = [] 24 | debug = [] 25 | android-lld = [] 26 | native-cpu = [] 27 | local_dynamic_tls = [] 28 | win8compat = [] 29 | usecxx17 = [] 30 | check = [] 31 | lto = [] 32 | notls = [] 33 | stats = [] 34 | usewait-on-address = [] 35 | libc-api = [] 36 | -------------------------------------------------------------------------------- /snmalloc-sys/build.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use std::env; 4 | 5 | #[derive(Debug, PartialEq)] 6 | enum Compiler { 7 | Clang, 8 | Gcc, 9 | Msvc, 10 | Unknown 11 | } 12 | 13 | struct BuildConfig { 14 | debug: bool, 15 | optim_level: String, 16 | target_os: String, 17 | target_env: String, 18 | target_family: String, 19 | target: String, 20 | out_dir: String, 21 | build_type: String, 22 | msystem: Option, 23 | cmake_cxx_standard: String, 24 | target_lib: String, 25 | features: BuildFeatures, 26 | #[cfg(feature = "build_cc")] 27 | builder: cc::Build, 28 | #[cfg(not(feature = "build_cc"))] 29 | builder: cmake::Config, 30 | compiler: Compiler 31 | } 32 | 33 | impl std::fmt::Debug for BuildConfig { 34 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 35 | f.debug_struct("BuildConfig") 36 | .field("debug", &self.debug) 37 | .field("optim_level", &self.optim_level) 38 | .field("target_os", &self.target_os) 39 | .field("target_env", &self.target_env) 40 | .field("target_family", &self.target_family) 41 | .field("target", &self.target) 42 | .field("out_dir", &self.out_dir) 43 | .field("build_type", &self.build_type) 44 | .field("msystem", &self.msystem) 45 | .field("cmake_cxx_standard", &self.cmake_cxx_standard) 46 | .field("target_lib", &self.target_lib) 47 | .field("features", &self.features) 48 | .finish() 49 | } 50 | } 51 | 52 | #[derive(Debug, Clone)] 53 | struct BuildFeatures { 54 | native_cpu: bool, 55 | qemu: bool, 56 | wait_on_address: bool, 57 | lto: bool, 58 | notls: bool, 59 | win8compat: bool, 60 | stats: bool, 61 | android_lld: bool, 62 | local_dynamic_tls: bool, 63 | libc_api: bool, 64 | } 65 | 66 | impl BuildConfig { 67 | fn new() -> Self { 68 | let debug = cfg!(feature = "debug"); 69 | #[cfg(feature = "build_cc")] 70 | let builder = cc::Build::new(); 71 | 72 | #[cfg(not(feature = "build_cc"))] 73 | let builder = Config::new("snmalloc"); 74 | 75 | let mut config = Self { 76 | debug, 77 | optim_level: (if debug { "-O0" } else { "-O3" }).to_string(), 78 | target_os: env::var("CARGO_CFG_TARGET_OS").expect("target_os not defined!"), 79 | target_env: env::var("CARGO_CFG_TARGET_ENV").expect("target_env not defined!"), 80 | target_family: env::var("CARGO_CFG_TARGET_FAMILY").expect("target family not set"), 81 | target: env::var("TARGET").expect("TARGET not set"), 82 | out_dir: env::var("OUT_DIR").unwrap(), 83 | build_type: (if debug { "Debug" } else { "Release" }).to_string(), 84 | msystem: env::var("MSYSTEM").ok(), 85 | cmake_cxx_standard: (if cfg!(feature = "usecxx17") { "17" } else { "20" }).to_string(), 86 | target_lib: (if cfg!(feature = "check") { 87 | "snmallocshim-checks-rust" 88 | } else { 89 | "snmallocshim-rust" 90 | }).to_string(), 91 | features: BuildFeatures::new(), 92 | builder, 93 | compiler: Compiler::Unknown, 94 | }; 95 | config.compiler = config.detect_compiler(); 96 | config.embed_build_info(); 97 | config 98 | } 99 | 100 | fn detect_compiler(&self) -> Compiler { 101 | // Check MSYSTEM for MSYS2 environments 102 | if let Some(msystem) = &self.msystem { 103 | match msystem.as_str() { 104 | "CLANG64" | "CLANGARM64" => return Compiler::Clang, 105 | "MINGW64" | "UCRT64" => return Compiler::Gcc, 106 | _ => {} 107 | } 108 | } 109 | 110 | // Check target environment 111 | if let Ok(env) = env::var("CARGO_CFG_TARGET_ENV") { 112 | match env.as_str() { 113 | "msvc" => return Compiler::Msvc, 114 | "gnu" => return Compiler::Gcc, 115 | _ => {} 116 | } 117 | } 118 | 119 | // Check CC environment variable 120 | if let Ok(cc) = env::var("CC") { 121 | let cc = cc.to_lowercase(); 122 | if cc.contains("clang") { 123 | return Compiler::Clang; 124 | } else if cc.contains("gcc") { 125 | return Compiler::Gcc; 126 | } 127 | } 128 | 129 | // Default based on platform and target 130 | if self.target.contains("msvc") { 131 | Compiler::Msvc 132 | } else if cfg!(windows) { 133 | Compiler::Gcc // Assume GCC for non-MSVC Windows environments 134 | } else if cfg!(unix) { 135 | Compiler::Clang // Default to Clang for Unix-like systems 136 | } else { 137 | Compiler::Unknown 138 | } 139 | } 140 | 141 | 142 | fn embed_build_info(&self) { 143 | let build_info = [ 144 | ("BUILD_TARGET_OS", &self.target_os), 145 | ("BUILD_TARGET_ENV", &self.target_env), 146 | ("BUILD_TARGET_FAMILY", &self.target_family), 147 | ("BUILD_TARGET", &self.target), 148 | ("BUILD_CC", &format!("{:#?}", self.compiler)), 149 | ("BUILD_TYPE", &self.build_type), 150 | ("BUILD_DEBUG", &self.debug.to_string()), 151 | ("BUILD_OPTIM_LEVEL", &self.optim_level), 152 | ("BUILD_CXX_STANDARD", &self.cmake_cxx_standard), 153 | ]; 154 | 155 | for (key, value) in build_info { 156 | println!("cargo:rustc-env={}={}", key, value); 157 | } 158 | 159 | if let Some(ms) = &self.msystem { 160 | println!("cargo:rustc-env=BUILD_MSYSTEM={}", ms); 161 | } 162 | } 163 | 164 | fn get_cpp_flags(&self) -> [&'static str; 2] { 165 | if cfg!(feature = "usecxx17") { 166 | ["-std=c++17", "/std:c++17"] 167 | } else { 168 | ["-std=c++20", "/std:c++20"] 169 | } 170 | } 171 | 172 | fn is_msvc(&self) -> bool { 173 | self.target_env == "msvc" 174 | } 175 | 176 | fn is_gnu(&self) -> bool { 177 | self.target_env == "gnu" 178 | } 179 | 180 | fn is_windows(&self) -> bool { 181 | self.target_os == "windows" 182 | } 183 | 184 | fn is_linux(&self) -> bool { 185 | self.target_os == "linux" 186 | } 187 | 188 | fn is_unix(&self) -> bool { 189 | self.target_family == "unix" 190 | } 191 | 192 | fn is_clang_msys(&self) -> bool { 193 | self.msystem.as_deref().map_or(false, |s| s.contains("CLANG")) 194 | } 195 | 196 | fn is_ucrt64(&self) -> bool { 197 | self.msystem.as_deref() == Some("UCRT64") 198 | } 199 | } 200 | 201 | trait BuilderDefine { 202 | fn define(&mut self, key: &str, value: &str) -> &mut Self; 203 | fn flag_if_supported(&mut self, flag: &str) -> &mut Self; 204 | fn build_lib(&mut self, target_lib: &str) -> std::path::PathBuf; 205 | fn configure_output_dir(&mut self, out_dir: &str) -> &mut Self; 206 | fn configure_cpp(&mut self, debug: bool) -> &mut Self; 207 | } 208 | 209 | #[cfg(feature = "build_cc")] 210 | impl BuilderDefine for cc::Build { 211 | fn define(&mut self, key: &str, value: &str) -> &mut Self { 212 | self.define(key, Some(value)) 213 | } 214 | 215 | fn flag_if_supported(&mut self, flag: &str) -> &mut Self { 216 | self.flag_if_supported(flag) 217 | } 218 | 219 | fn build_lib(&mut self, target_lib: &str) -> std::path::PathBuf { 220 | self.compile(target_lib); 221 | std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap()) 222 | } 223 | 224 | fn configure_output_dir(&mut self, out_dir: &str) -> &mut Self { 225 | self.out_dir(out_dir) 226 | } 227 | 228 | fn configure_cpp(&mut self, debug: bool) -> &mut Self { 229 | self.include("snmalloc/src") 230 | .file("snmalloc/src/snmalloc/override/rust.cc") 231 | .cpp(true) 232 | .debug(debug) 233 | .static_crt(true) 234 | } 235 | } 236 | 237 | #[cfg(not(feature = "build_cc"))] 238 | impl BuilderDefine for cmake::Config { 239 | fn define(&mut self, key: &str, value: &str) -> &mut Self { 240 | self.define(key, value) 241 | } 242 | 243 | fn flag_if_supported(&mut self, _flag: &str) -> &mut Self { 244 | self 245 | } 246 | 247 | fn build_lib(&mut self, target_lib: &str) -> std::path::PathBuf { 248 | self.build_target(target_lib).build() 249 | } 250 | 251 | fn configure_output_dir(&mut self, out_dir: &str) -> &mut Self { 252 | self.out_dir(out_dir) 253 | } 254 | 255 | fn configure_cpp(&mut self, _debug: bool) -> &mut Self { 256 | self.define("SNMALLOC_RUST_SUPPORT", "ON") 257 | .very_verbose(true) 258 | .define("CMAKE_SH", "CMAKE_SH-NOTFOUND") 259 | .always_configure(true) 260 | .static_crt(true) 261 | } 262 | } 263 | 264 | fn apply_defines(builder: &mut T, defines: &[(&str, &str)]) { 265 | for (key, value) in defines { 266 | builder.define(key, value); 267 | } 268 | } 269 | impl BuildFeatures { 270 | fn new() -> Self { 271 | Self { 272 | native_cpu: cfg!(feature = "native-cpu"), 273 | qemu: cfg!(feature = "qemu"), 274 | wait_on_address: cfg!(feature = "usewait-on-address"), 275 | lto: cfg!(feature = "lto"), 276 | notls: cfg!(feature = "notls"), 277 | win8compat: cfg!(feature = "win8compat"), 278 | stats: cfg!(feature = "stats"), 279 | android_lld: cfg!(feature = "android-lld"), 280 | local_dynamic_tls: cfg!(feature = "local_dynamic_tls"), 281 | libc_api: cfg!(feature = "libc-api"), 282 | } 283 | } 284 | } 285 | 286 | fn configure_platform(config: &mut BuildConfig) { 287 | // Basic optimization and compiler flags 288 | config.builder 289 | .flag_if_supported(&config.optim_level) 290 | .flag_if_supported("-fomit-frame-pointer"); 291 | 292 | // C++ standard flags 293 | for std in config.get_cpp_flags() { 294 | config.builder.flag_if_supported(std); 295 | } 296 | 297 | // Common feature configurations 298 | if config.features.native_cpu { 299 | config.builder.define("SNMALLOC_OPTIMISE_FOR_CURRENT_MACHINE", "ON"); 300 | #[cfg(feature = "build_cc")] 301 | config.builder.flag_if_supported("-march=native"); 302 | } 303 | 304 | // Platform-specific configurations 305 | match () { 306 | _ if config.is_windows() => { 307 | let common_flags = vec!["-mcx16", "-fno-exceptions", "-fno-rtti", "-pthread"]; 308 | for flag in common_flags { 309 | config.builder.flag_if_supported(flag); 310 | } 311 | 312 | if let Some(msystem) = &config.msystem { 313 | match msystem.as_str() { 314 | "CLANG64" | "CLANGARM64" => { 315 | let defines = vec![ 316 | ("CMAKE_CXX_COMPILER", "clang++"), 317 | ("CMAKE_C_COMPILER", "clang"), 318 | ("CMAKE_CXX_FLAGS", "-fuse-ld=lld -stdlib=libc++ -mcx16 -Wno-error=unknown-pragmas -Qunused-arguments"), 319 | ("CMAKE_C_FLAGS", "-fuse-ld=lld -Wno-error=unknown-pragmas -Qunused-arguments"), 320 | ("CMAKE_EXE_LINKER_FLAGS", "-fuse-ld=lld -stdlib=libc++"), 321 | ("CMAKE_SHARED_LINKER_FLAGS", "-fuse-ld=lld -stdlib=libc++") 322 | ]; 323 | apply_defines(&mut config.builder, &defines); 324 | if config.features.lto { 325 | config.builder.flag_if_supported("-flto=thin"); 326 | } 327 | } 328 | "UCRT64" | "MINGW64" => { 329 | let defines = vec![ 330 | ("CMAKE_CXX_FLAGS", "-fuse-ld=lld -Wno-error=unknown-pragmas"), 331 | ("CMAKE_SYSTEM_NAME", "Windows"), 332 | ("CMAKE_C_FLAGS", "-fuse-ld=lld -Wno-error=unknown-pragmas"), 333 | ("CMAKE_EXE_LINKER_FLAGS", "-fuse-ld=lld"), 334 | ("CMAKE_SHARED_LINKER_FLAGS", "-fuse-ld=lld") 335 | ]; 336 | apply_defines(&mut config.builder, &defines); 337 | } 338 | _ => {} 339 | } 340 | } 341 | } 342 | _ if config.is_msvc() => { 343 | let msvc_flags = vec![ 344 | "/nologo", "/W4", "/WX", "/wd4127", "/wd4324", "/wd4201", 345 | "/Ob2", "/DNDEBUG", "/EHsc", "/Gd", "/TP", "/Gm-", "/GS", 346 | "/fp:precise", "/Zc:wchar_t", "/Zc:forScope", "/Zc:inline" 347 | ]; 348 | for flag in msvc_flags { 349 | config.builder.flag_if_supported(flag); 350 | } 351 | 352 | if config.features.lto { 353 | config.builder 354 | .flag_if_supported("/GL") 355 | .define("CMAKE_INTERPROCEDURAL_OPTIMIZATION", "TRUE") 356 | .define("SNMALLOC_IPO", "ON"); 357 | println!("cargo:rustc-link-arg=/LTCG"); 358 | } 359 | 360 | config.builder 361 | .define("CMAKE_CXX_FLAGS_RELEASE", "/O2 /Ob2 /DNDEBUG /EHsc") 362 | .define("CMAKE_C_FLAGS_RELEASE", "/O2 /Ob2 /DNDEBUG /EHsc"); 363 | } 364 | _ if config.is_unix() => { 365 | let unix_flags = vec!["-fPIC", "-pthread", "-fno-exceptions", "-fno-rtti", "-mcx16", "-Wno-unused-parameter"]; 366 | for flag in unix_flags { 367 | config.builder.flag_if_supported(flag); 368 | } 369 | 370 | if config.target_os != "haiku" { 371 | let tls_model = if config.features.local_dynamic_tls { "-ftls-model=local-dynamic" } else { "-ftls-model=initial-exec" }; 372 | config.builder.flag_if_supported(tls_model); 373 | } 374 | } 375 | _ => {} 376 | } 377 | 378 | // Feature configurations 379 | config.builder 380 | .define("SNMALLOC_QEMU_WORKAROUND", if config.features.qemu { "ON" } else { "OFF" }) 381 | .define("SNMALLOC_ENABLE_DYNAMIC_LOADING", if config.features.notls { "ON" } else { "OFF" }) 382 | .define("SNMALLOC_USE_WAIT_ON_ADDRESS", if config.features.wait_on_address { "1" } else { "0" }) 383 | .define("USE_SNMALLOC_STATS", if config.features.stats { "ON" } else { "OFF" }) 384 | .define("SNMALLOC_RUST_LIBC_API", if config.features.libc_api { "ON" } else { "OFF" }); 385 | 386 | // Android configuration 387 | if config.target.contains("android") { 388 | let ndk = env::var("ANDROID_NDK").expect("ANDROID_NDK environment variable not set"); 389 | config.builder 390 | .define("CMAKE_TOOLCHAIN_FILE", &*format!("{}/build/cmake/android.toolchain.cmake", ndk)) 391 | .define("ANDROID_PLATFORM", &*env::var("ANDROID_PLATFORM").unwrap_or_default()); 392 | 393 | if cfg!(feature = "android-lld") { 394 | config.builder.define("ANDROID_LD", "lld"); 395 | } 396 | 397 | let (abi, arm_mode) = match config.target.as_str() { 398 | t if t.contains("aarch64") => ("arm64-v8a", None), 399 | t if t.contains("armv7") => ("armeabi-v7a", Some("arm")), 400 | t if t.contains("x86_64") => ("x86_64", None), 401 | t if t.contains("i686") => ("x86", None), 402 | t if t.contains("neon") => ("armeabi-v7a with NEON", None), 403 | t if t.contains("arm") => ("armeabi-v7a", None), 404 | _ => panic!("Unsupported Android architecture: {}", config.target), 405 | }; 406 | config.builder.define("ANDROID_ABI", abi); 407 | if let Some(mode) = arm_mode { 408 | config.builder.define("ANDROID_ARM_MODE", mode); 409 | } 410 | } 411 | } 412 | 413 | 414 | fn configure_linking(config: &BuildConfig) { 415 | 416 | match () { 417 | _ if config.is_msvc() => { 418 | // Windows MSVC specific libraries 419 | if !config.features.win8compat { 420 | println!("cargo:rustc-link-lib=mincore"); 421 | } 422 | // Essential Windows libraries 423 | println!("cargo:rustc-link-lib=kernel32"); 424 | println!("cargo:rustc-link-lib=user32"); 425 | println!("cargo:rustc-link-lib=advapi32"); 426 | println!("cargo:rustc-link-lib=ws2_32"); 427 | println!("cargo:rustc-link-lib=userenv"); 428 | println!("cargo:rustc-link-lib=bcrypt"); 429 | println!("cargo:rustc-link-lib=msvcrt"); 430 | } 431 | _ if config.is_windows() && config.is_gnu() => { 432 | println!("cargo:rustc-link-lib=kernel32"); 433 | println!("cargo:rustc-link-lib=bcrypt"); 434 | println!("cargo:rustc-link-lib=winpthread"); 435 | 436 | if config.is_clang_msys() { 437 | println!("cargo:rustc-link-lib=c++"); 438 | } else if config.is_ucrt64() { 439 | println!("cargo:rustc-link-lib=stdc++"); 440 | } else { 441 | println!("cargo:rustc-link-lib=stdc++"); 442 | println!("cargo:rustc-link-lib=atomic"); 443 | } 444 | } 445 | _ if cfg!(target_os = "freebsd") => { 446 | println!("cargo:rustc-link-lib=c++"); 447 | } 448 | _ if config.is_linux() => { 449 | println!("cargo:rustc-link-lib=atomic"); 450 | println!("cargo:rustc-link-lib=stdc++"); 451 | println!("cargo:rustc-link-lib=pthread"); 452 | println!("cargo:rustc-link-lib=c"); 453 | println!("cargo:rustc-link-lib=gcc_s"); 454 | println!("cargo:rustc-link-lib=util"); 455 | println!("cargo:rustc-link-lib=rt"); 456 | println!("cargo:rustc-link-lib=dl"); 457 | println!("cargo:rustc-link-lib=m"); 458 | 459 | if cfg!(feature = "usecxx17") && !config.is_clang_msys() { 460 | println!("cargo:rustc-link-lib=gcc"); 461 | } 462 | } 463 | _ if config.is_unix() && !cfg!(any(target_os = "macos", target_os = "freebsd")) => { 464 | if config.is_gnu() { 465 | println!("cargo:rustc-link-lib=c_nonshared"); 466 | } 467 | } 468 | _ if !config.is_windows() => { 469 | let cxxlib = if cfg!(any(target_os = "macos", target_os = "openbsd")) { 470 | "c++" 471 | } else { 472 | "stdc++" 473 | }; 474 | println!("cargo:rustc-link-lib={}", cxxlib); 475 | } 476 | _ => {} 477 | } 478 | } 479 | 480 | #[cfg(feature = "build_cc")] 481 | use cc; 482 | #[cfg(not(feature = "build_cc"))] 483 | use cmake::Config; 484 | 485 | fn main() { 486 | let mut config = BuildConfig::new(); 487 | 488 | config.builder 489 | .configure_cpp(config.debug) 490 | .configure_output_dir(&config.out_dir); 491 | 492 | // Apply all configurations 493 | configure_platform(&mut config); 494 | 495 | // Build and configure output 496 | println!("cargo:rustc-link-search=/usr/local/lib"); 497 | println!("cargo:rustc-link-search={}", config.out_dir); 498 | println!("cargo:rustc-link-search={}/build", config.out_dir); 499 | println!("cargo:rustc-link-search={}/build/Debug", config.out_dir); 500 | println!("cargo:rustc-link-search={}/build/Release", config.out_dir); 501 | let mut _dst = config.builder.build_lib(&config.target_lib); 502 | println!("cargo:rustc-link-lib={}", config.target_lib); 503 | configure_linking(&config); 504 | } 505 | -------------------------------------------------------------------------------- /snmalloc-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![allow(non_camel_case_types)] 3 | 4 | use core::ffi::c_void; 5 | 6 | extern "C" { 7 | /// Allocate the memory with the given alignment and size. 8 | /// On success, it returns a pointer pointing to the required memory address. 9 | /// On failure, it returns a null pointer. 10 | /// The client must assure the following things: 11 | /// - `alignment` is greater than zero 12 | /// - `alignment` is a power of 2 13 | /// The program may be forced to abort if the constrains are not full-filled. 14 | pub fn sn_rust_alloc(alignment: usize, size: usize) -> *mut c_void; 15 | 16 | /// De-allocate the memory at the given address with the given alignment and size. 17 | /// The client must assure the following things: 18 | /// - the memory is acquired using the same allocator and the pointer points to the start position. 19 | /// - `alignment` and `size` is the same as allocation 20 | /// The program may be forced to abort if the constrains are not full-filled. 21 | pub fn sn_rust_dealloc(ptr: *mut c_void, alignment: usize, size: usize) -> c_void; 22 | 23 | /// Behaves like rust_alloc, but also ensures that the contents are set to zero before being returned. 24 | pub fn sn_rust_alloc_zeroed(alignment: usize, size: usize) -> *mut c_void; 25 | 26 | /// Re-allocate the memory at the given address with the given alignment and size. 27 | /// On success, it returns a pointer pointing to the required memory address. 28 | /// The memory content within the `new_size` will remains the same as previous. 29 | /// On failure, it returns a null pointer. In this situation, the previous memory is not returned to the allocator. 30 | /// The client must assure the following things: 31 | /// - the memory is acquired using the same allocator and the pointer points to the start position 32 | /// - `alignment` and `old_size` is the same as allocation 33 | /// - `alignment` fulfills all the requirements as `rust_alloc` 34 | /// The program may be forced to abort if the constrains are not full-filled. 35 | pub fn sn_rust_realloc( 36 | ptr: *mut c_void, 37 | alignment: usize, 38 | old_size: usize, 39 | new_size: usize, 40 | ) -> *mut c_void; 41 | 42 | /// Return the available bytes in a memory block. 43 | pub fn sn_rust_usable_size(p: *const c_void) -> usize; 44 | } 45 | 46 | #[cfg(feature = "libc-api")] 47 | extern "C" { 48 | /// Allocate `count` items of `size` length each. 49 | /// Returns `null` if `count * size` overflows or on out-of-memory. 50 | /// All items are initialized to zero. 51 | pub fn sn_calloc(count: usize, size: usize) -> *mut c_void; 52 | 53 | /// Allocate `size` bytes. 54 | /// Returns pointer to the allocated memory or null if out of memory. 55 | /// Returns a unique pointer if called with `size` 0. 56 | pub fn sn_malloc(size: usize) -> *mut c_void; 57 | 58 | /// Re-allocate memory to `newsize` bytes. 59 | /// Return pointer to the allocated memory or null if out of memory. If null 60 | /// is returned, the pointer `p` is not freed. Otherwise the original 61 | /// pointer is either freed or returned as the reallocated result (in case 62 | /// it fits in-place with the new size). 63 | /// If `p` is null, it behaves as [`sn_malloc`]. If `newsize` is larger than 64 | /// the original `size` allocated for `p`, the bytes after `size` are 65 | /// uninitialized. 66 | pub fn sn_realloc(p: *mut c_void, newsize: usize) -> *mut c_void; 67 | 68 | /// Free previously allocated memory. 69 | /// The pointer `p` must have been allocated before (or be null). 70 | pub fn sn_free(p: *mut c_void); 71 | 72 | /// Return the available bytes in a memory block. 73 | pub fn sn_malloc_usable_size(p: *const c_void) -> usize; 74 | 75 | } 76 | 77 | #[cfg(test)] 78 | mod rust_tests { 79 | use super::*; 80 | 81 | #[test] 82 | fn it_zero_allocs_correctly() { 83 | let ptr = unsafe { sn_rust_alloc_zeroed(8, 1024) } as *mut u8 as *mut [u8; 1024]; 84 | unsafe { 85 | assert!((*ptr).iter().all(|x| *x == 0)); 86 | }; 87 | unsafe { sn_rust_dealloc(ptr as *mut c_void, 8, 1024) }; 88 | } 89 | 90 | #[test] 91 | fn it_frees_memory_malloc() { 92 | let ptr = unsafe { sn_rust_alloc(8, 8) } as *mut u8; 93 | unsafe { 94 | *ptr = 127; 95 | assert_eq!(*ptr, 127) 96 | }; 97 | unsafe { sn_rust_dealloc(ptr as *mut c_void, 8, 8) }; 98 | } 99 | 100 | #[test] 101 | fn it_reallocs_correctly() { 102 | let mut ptr = unsafe { sn_rust_alloc(8, 8) } as *mut u8; 103 | unsafe { 104 | *ptr = 127; 105 | assert_eq!(*ptr, 127) 106 | }; 107 | ptr = unsafe { sn_rust_realloc(ptr as *mut c_void, 8, 8, 16) } as *mut u8; 108 | unsafe { assert_eq!(*ptr, 127) }; 109 | unsafe { sn_rust_dealloc(ptr as *mut c_void, 8, 16) }; 110 | } 111 | 112 | #[test] 113 | fn it_calculates_usable_size() { 114 | let ptr = unsafe { sn_rust_alloc(32, 8) } as *mut u8; 115 | let usable_size = unsafe { sn_rust_usable_size(ptr as *mut c_void) }; 116 | assert!( 117 | usable_size >= 32, 118 | "usable_size should at least equal to the allocated size" 119 | ); 120 | unsafe { sn_rust_dealloc(ptr as *mut c_void, 32, 8) }; 121 | } 122 | } 123 | 124 | #[cfg(all(test, feature = "libc-api"))] 125 | mod libc_tests { 126 | use super::*; 127 | 128 | #[test] 129 | fn it_frees_memory_sn_malloc() { 130 | let ptr = unsafe { sn_malloc(8) } as *mut u8; 131 | unsafe { sn_free(ptr as *mut c_void) }; 132 | } 133 | 134 | #[test] 135 | fn it_frees_memory_sn_realloc() { 136 | let ptr = unsafe { sn_malloc(8) } as *mut u8; 137 | let ptr = unsafe { sn_realloc(ptr as *mut c_void, 8) } as *mut u8; 138 | unsafe { sn_free(ptr as *mut c_void) }; 139 | } 140 | 141 | #[test] 142 | fn it_calculates_malloc_usable_size() { 143 | let ptr = unsafe { sn_malloc(32) } as *mut u8; 144 | let usable_size = unsafe { sn_malloc_usable_size(ptr as *mut c_void) }; 145 | assert!( 146 | usable_size >= 32, 147 | "usable_size should at least equal to the allocated size" 148 | ); 149 | unsafe { sn_free(ptr as *mut c_void) }; 150 | } 151 | } -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | //! `snmalloc-rs` provides a wrapper for [`microsoft/snmalloc`](https://github.com/microsoft/snmalloc) to make it usable as a global allocator for rust. 3 | //! snmalloc is a research allocator. Its key design features are: 4 | //! - Memory that is freed by the same thread that allocated it does not require any synchronising operations. 5 | //! - Freeing memory in a different thread to initially allocated it, does not take any locks and instead uses a novel message passing scheme to return the memory to the original allocator, where it is recycled. 6 | //! - The allocator uses large ranges of pages to reduce the amount of meta-data required. 7 | //! 8 | //! The benchmark is available at the [paper](https://github.com/microsoft/snmalloc/blob/master/snmalloc.pdf) of `snmalloc` 9 | //! There are three features defined in this crate: 10 | //! - `debug`: Enable the `Debug` mode in `snmalloc`. 11 | //! - `1mib`: Use the `1mib` chunk configuration. 12 | //! - `cache-friendly`: Make the allocator more cache friendly (setting `CACHE_FRIENDLY_OFFSET` to `64` in building the library). 13 | //! 14 | //! The whole library supports `no_std`. 15 | //! 16 | //! To use `snmalloc-rs` add it as a dependency: 17 | //! ```toml 18 | //! # Cargo.toml 19 | //! [dependencies] 20 | //! snmalloc-rs = "0.1.0" 21 | //! ``` 22 | //! 23 | //! To set `SnMalloc` as the global allocator add this to your project: 24 | //! ```rust 25 | //! #[global_allocator] 26 | //! static ALLOC: snmalloc_rs::SnMalloc = snmalloc_rs::SnMalloc; 27 | //! ``` 28 | extern crate snmalloc_sys as ffi; 29 | 30 | use core::{ 31 | alloc::{GlobalAlloc, Layout}, 32 | ptr::NonNull, 33 | }; 34 | 35 | #[derive(Debug, Copy, Clone)] 36 | #[repr(C)] 37 | pub struct SnMalloc; 38 | 39 | unsafe impl Send for SnMalloc {} 40 | unsafe impl Sync for SnMalloc {} 41 | 42 | impl SnMalloc { 43 | #[inline(always)] 44 | pub const fn new() -> Self { 45 | Self 46 | } 47 | 48 | /// Returns the available bytes in a memory block. 49 | #[inline(always)] 50 | pub fn usable_size(&self, ptr: *const u8) -> Option { 51 | match ptr.is_null() { 52 | true => None, 53 | false => Some(unsafe { ffi::sn_rust_usable_size(ptr.cast()) }) 54 | } 55 | } 56 | 57 | /// Allocates memory with the given layout, returning a non-null pointer on success 58 | #[inline(always)] 59 | pub fn alloc_aligned(&self, layout: Layout) -> Option> { 60 | match layout.size() { 61 | 0 => NonNull::new(layout.align() as *mut u8), 62 | size => NonNull::new(unsafe { ffi::sn_rust_alloc(layout.align(), size) }.cast()) 63 | } 64 | } 65 | } 66 | 67 | unsafe impl GlobalAlloc for SnMalloc { 68 | /// Allocate the memory with the given alignment and size. 69 | /// On success, it returns a pointer pointing to the required memory address. 70 | /// On failure, it returns a null pointer. 71 | /// The client must assure the following things: 72 | /// - `alignment` is greater than zero 73 | /// - Other constrains are the same as the rust standard library. 74 | /// 75 | /// The program may be forced to abort if the constrains are not full-filled. 76 | #[inline(always)] 77 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 78 | match layout.size() { 79 | 0 => layout.align() as *mut u8, 80 | size => ffi::sn_rust_alloc(layout.align(), size).cast() 81 | } 82 | } 83 | 84 | /// De-allocate the memory at the given address with the given alignment and size. 85 | /// The client must assure the following things: 86 | /// - the memory is acquired using the same allocator and the pointer points to the start position. 87 | /// - Other constrains are the same as the rust standard library. 88 | /// 89 | /// The program may be forced to abort if the constrains are not full-filled. 90 | #[inline(always)] 91 | unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { 92 | if layout.size() != 0 { 93 | ffi::sn_rust_dealloc(ptr as _, layout.align(), layout.size()); 94 | } 95 | } 96 | 97 | /// Behaves like alloc, but also ensures that the contents are set to zero before being returned. 98 | #[inline(always)] 99 | unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { 100 | match layout.size() { 101 | 0 => layout.align() as *mut u8, 102 | size => ffi::sn_rust_alloc_zeroed(layout.align(), size).cast() 103 | } 104 | } 105 | 106 | /// Re-allocate the memory at the given address with the given alignment and size. 107 | /// On success, it returns a pointer pointing to the required memory address. 108 | /// The memory content within the `new_size` will remains the same as previous. 109 | /// On failure, it returns a null pointer. In this situation, the previous memory is not returned to the allocator. 110 | /// The client must assure the following things: 111 | /// - the memory is acquired using the same allocator and the pointer points to the start position 112 | /// - `alignment` fulfills all the requirements as `rust_alloc` 113 | /// - Other constrains are the same as the rust standard library. 114 | /// 115 | /// The program may be forced to abort if the constrains are not full-filled. 116 | #[inline(always)] 117 | unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { 118 | match new_size { 119 | 0 => { 120 | self.dealloc(ptr, layout); 121 | layout.align() as *mut u8 122 | } 123 | new_size if layout.size() == 0 => { 124 | self.alloc(Layout::from_size_align_unchecked(new_size, layout.align())) 125 | } 126 | _ => ffi::sn_rust_realloc(ptr.cast(), layout.align(), layout.size(), new_size).cast() 127 | } 128 | } 129 | } 130 | 131 | #[cfg(test)] 132 | mod tests { 133 | use super::*; 134 | #[test] 135 | fn allocation_lifecycle() { 136 | let alloc = SnMalloc::new(); 137 | unsafe { 138 | let layout = Layout::from_size_align(8, 8).unwrap(); 139 | 140 | // Test regular allocation 141 | let ptr = alloc.alloc(layout); 142 | alloc.dealloc(ptr, layout); 143 | 144 | // Test zeroed allocation 145 | let ptr = alloc.alloc_zeroed(layout); 146 | alloc.dealloc(ptr, layout); 147 | 148 | // Test reallocation 149 | let ptr = alloc.alloc(layout); 150 | let ptr = alloc.realloc(ptr, layout, 16); 151 | alloc.dealloc(ptr, layout); 152 | 153 | // Test large allocation 154 | let large_layout = Layout::from_size_align(1 << 20, 32).unwrap(); 155 | let ptr = alloc.alloc(large_layout); 156 | alloc.dealloc(ptr, large_layout); 157 | } 158 | } 159 | #[test] 160 | fn it_frees_allocated_memory() { 161 | unsafe { 162 | let layout = Layout::from_size_align(8, 8).unwrap(); 163 | let alloc = SnMalloc; 164 | 165 | let ptr = alloc.alloc(layout); 166 | alloc.dealloc(ptr, layout); 167 | } 168 | } 169 | 170 | #[test] 171 | fn it_frees_zero_allocated_memory() { 172 | unsafe { 173 | let layout = Layout::from_size_align(8, 8).unwrap(); 174 | let alloc = SnMalloc; 175 | 176 | let ptr = alloc.alloc_zeroed(layout); 177 | alloc.dealloc(ptr, layout); 178 | } 179 | } 180 | 181 | #[test] 182 | fn it_frees_reallocated_memory() { 183 | unsafe { 184 | let layout = Layout::from_size_align(8, 8).unwrap(); 185 | let alloc = SnMalloc; 186 | 187 | let ptr = alloc.alloc(layout); 188 | let ptr = alloc.realloc(ptr, layout, 16); 189 | alloc.dealloc(ptr, layout); 190 | } 191 | } 192 | 193 | #[test] 194 | fn it_frees_large_alloc() { 195 | unsafe { 196 | let layout = Layout::from_size_align(1 << 20, 32).unwrap(); 197 | let alloc = SnMalloc; 198 | 199 | let ptr = alloc.alloc(layout); 200 | alloc.dealloc(ptr, layout); 201 | } 202 | } 203 | 204 | #[test] 205 | fn test_usable_size() { 206 | let alloc = SnMalloc::new(); 207 | unsafe { 208 | let layout = Layout::from_size_align(8, 8).unwrap(); 209 | let ptr = alloc.alloc(layout); 210 | let usz = alloc.usable_size(ptr).expect("usable_size returned None"); 211 | alloc.dealloc(ptr, layout); 212 | assert!(usz >= 8); 213 | } 214 | } 215 | } 216 | --------------------------------------------------------------------------------