├── .appveyor.yml ├── .github └── workflows │ ├── audit-on-push.yml │ ├── coverage.yml │ ├── fmt.yml │ └── rust.yml ├── .gitignore ├── .travis.yml ├── Cargo.lock ├── Cargo.toml ├── README.md ├── audit-on-push.yml ├── benches ├── alloc.rs ├── get.rs ├── index.rs ├── insert.rs ├── iter.rs ├── pop_back.rs ├── pop_front.rs ├── push_back.rs ├── push_front.rs └── remove.rs ├── bors.toml ├── coverage.yml ├── license-apache.md ├── license-mit.md ├── license.md ├── rustfmt.toml ├── san ├── Cargo.toml └── src │ └── main.rs └── src ├── lib.rs ├── macros.rs └── mirrored ├── buffer.rs ├── linux.rs ├── macos.rs ├── mod.rs ├── sysv.rs └── winapi.rs /.appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | # We don't want to do identical comdat folding as it messes up the ability to 3 | # generate lossless backtraces in some cases. This is enabled by rustc by 4 | # default so pass a flag to disable it to ensure our tests work ok. 5 | RUSTFLAGS: -Clink-args=/OPT:NOICF 6 | 7 | matrix: 8 | - TARGET: x86_64-pc-windows-msvc 9 | 10 | install: 11 | # Install rust, x86_64-pc-windows-msvc host 12 | - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe 13 | - rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain nightly 14 | - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin 15 | - if NOT "%TARGET%" == "x86_64-pc-windows-msvc" rustup target add %TARGET% 16 | - rustc -vV 17 | - cargo -vV 18 | 19 | build: false 20 | 21 | test_script: 22 | - C:\msys64\usr\bin\sh ci\run.sh 23 | -------------------------------------------------------------------------------- /.github/workflows/audit-on-push.yml: -------------------------------------------------------------------------------- 1 | name: Security audit 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - dev 7 | paths: 8 | - '**/Cargo.toml' 9 | - '**/Cargo.lock' 10 | jobs: 11 | security_audit: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v1 15 | - uses: actions-rs/audit-check@v1 16 | with: 17 | token: ${{ secrets.GITHUB_TOKEN }} 18 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | - dev 6 | pull_request: 7 | branches: 8 | - master 9 | - dev 10 | 11 | name: Code Coverage 12 | jobs: 13 | coverage: 14 | name: Run test coverage 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v1 18 | - name: Cache cargo registry 19 | uses: actions/cache@v1 20 | with: 21 | path: ~/.cargo/registry 22 | key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} 23 | - name: Cache cargo index 24 | uses: actions/cache@v1 25 | with: 26 | path: ~/.cargo/git 27 | key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} 28 | - name: Cache cargo binaries 29 | uses: actions/cache@v1 30 | with: 31 | path: ~/.cargo/bin 32 | key: ${{ runner.os }}-cargo-bin-${{ hashFiles('**/Cargo.lock') }} 33 | - name: Toolchain setup 34 | uses: actions-rs/toolchain@v1 35 | with: 36 | toolchain: nightly 37 | override: true 38 | components: llvm-tools-preview 39 | - name: Install grcov 40 | run: if [[ ! -e ~/.cargo/bin/grcov ]]; then cargo install grcov; fi 41 | - name: Updating package repo 42 | run: sudo apt-get update 43 | - name: Installing dependencies 44 | run: sudo apt-get install libasound2-dev 45 | - name: Run tests 46 | run: cargo test 47 | env: 48 | RUSTFLAGS: '-Zinstrument-coverage' 49 | LLVM_PROFILE_FILE: 'report-%p-%m.profraw' 50 | - name: Run grcov 51 | run: grcov . --binary-path target/debug/deps/ -s . -t lcov --branch --ignore-not-existing --ignore '../**' --ignore '/*' -o coverage.lcov 52 | - name: Coveralls upload 53 | uses: coverallsapp/github-action@master 54 | with: 55 | github-token: ${{ secrets.GITHUB_TOKEN }} 56 | path-to-lcov: coverage.lcov 57 | -------------------------------------------------------------------------------- /.github/workflows/fmt.yml: -------------------------------------------------------------------------------- 1 | name: Rust fmt and clippy 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | - dev 9 | - "*_impl" 10 | 11 | env: 12 | CARGO_INCREMENTAL: 0 13 | CARGO_TERM_COLOR: always 14 | 15 | jobs: 16 | checks: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | - name: Cache 21 | uses: actions/cache@v3 22 | with: 23 | path: | 24 | ~/.cargo/git 25 | ~/.cargo/registry 26 | ~/.cargo/bin 27 | key: fmt-clippy-cargo-${{ hashFiles('**/Cargo.lock') }} 28 | - name: Setup toolchain 29 | uses: actions-rs/toolchain@v1 30 | with: 31 | toolchain: stable 32 | components: clippy, rustfmt 33 | override: true 34 | - name: Formatting 35 | run: cargo fmt -- --check 36 | - name: Check (all features) 37 | run: cargo check --locked --all-features 38 | - name: Clippy (all features) 39 | run: cargo clippy --locked --all-features 40 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | - dev 9 | - "*_impl" 10 | 11 | env: 12 | CARGO_INCREMENTAL: 0 13 | CARGO_TERM_COLOR: always 14 | 15 | jobs: 16 | tests: 17 | name: Tests 18 | runs-on: ${{ matrix.os }} 19 | continue-on-error: ${{ matrix.toolchain == 'nightly' }} 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | os: [ubuntu-latest] 24 | toolchain: [stable, beta, nightly] 25 | target: [ 26 | aarch64-unknown-linux-gnu, 27 | arm-unknown-linux-gnueabi, 28 | arm-unknown-linux-musleabi, 29 | armv7-unknown-linux-gnueabihf, 30 | armv7-unknown-linux-musleabihf, 31 | i586-unknown-linux-gnu, 32 | i686-unknown-linux-gnu, 33 | i686-unknown-linux-musl, 34 | powerpc-unknown-linux-gnu, 35 | powerpc64-unknown-linux-gnu, 36 | powerpc64le-unknown-linux-gnu, 37 | x86_64-unknown-linux-gnu, 38 | x86_64-unknown-linux-musl, 39 | aarch64-linux-android, 40 | arm-linux-androideabi, 41 | armv7-linux-androideabi, 42 | x86_64-linux-android 43 | ] 44 | include: 45 | - os: ubuntu-latest 46 | steps: 47 | - uses: actions/checkout@v4 48 | - name: Cache 49 | uses: actions/cache@v3 50 | with: 51 | path: | 52 | ~/.cargo/git 53 | ~/.cargo/registry 54 | ~/.cargo/bin 55 | key: ${{ matrix.os }}-${{ matrix.target }}-${{ matrix.toolchain }}-cargo-${{ hashFiles('**/Cargo.lock') }} 56 | - name: Setup toolchain 57 | if: ${{ matrix.toolchain != 'nightly' }} 58 | uses: actions-rs/toolchain@v1 59 | with: 60 | toolchain: ${{ matrix.toolchain }} 61 | target: ${{ matrix.target }} 62 | override: true 63 | - name: Setup toolchain 64 | if: ${{ matrix.toolchain == 'nightly' }} 65 | uses: actions-rs/toolchain@v1 66 | with: 67 | toolchain: ${{ matrix.toolchain }} 68 | target: ${{ matrix.target }} 69 | profile: minimal 70 | override: true 71 | - name: Set ulimit 72 | run: ulimit -Sn 4096 73 | - name: Test (all features) 74 | env: 75 | TARGET: ${{ matrix.target }} 76 | run: cargo test --locked --all-features 77 | - name: Build 78 | env: 79 | TARGET: ${{ matrix.target }} 80 | run: cargo build --release --locked 81 | - name: Build (all features) 82 | env: 83 | TARGET: ${{ matrix.target }} 84 | run: cargo build --release --locked --all-features 85 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: nightly 3 | services: docker 4 | matrix: 5 | fast_finish: true 6 | allow_failures: 7 | # Travis runs out of file-descriptors for some reason... 8 | - env: TARGET=x86_64-apple-darwin SYSV=1 9 | # These did never work 10 | - env: TARGET=sparc64-unknown-linux-gnu NOSTD=1 11 | # These are missing libc sysV functions: 12 | - env: TARGET=x86_64-unknown-netbsd NORUN=1 13 | - env: TARGET=x86_64-sun-solaris NORUN=1 14 | # Android does not support SystemV shared memory?: 15 | - env: TARGET=aarch64-linux-android SYSV=1 16 | - env: DOCS/COVERAGE 17 | include: 18 | - name: "rustfmt" 19 | install: rustup component add rustfmt-preview 20 | script: cargo fmt --all -- --check 21 | - name: "clippy" 22 | install: rustup component add clippy-preview 23 | script: cargo clippy -- -D clippy::pedantic 24 | - name: "documentation + code coverage" 25 | sudo: required 26 | install: 27 | - export PATH=$HOME/.cargo/bin:$PATH 28 | - cargo install cargo-update || echo "cargo-update already installed" 29 | - cargo install cargo-travis || echo "cargo-travis already installed" 30 | - cargo install-update -a 31 | script: 32 | - | 33 | cargo build --verbose && 34 | cargo coverage --features "unstable" --verbose && 35 | bash <(curl -s https://codecov.io/bash) -s target/kcov 36 | - | 37 | cargo doc --verbose --no-deps && 38 | cargo doc-upload 39 | addons: 40 | apt: 41 | packages: 42 | - libcurl4-openssl-dev 43 | - libelf-dev 44 | - libdw-dev 45 | - binutils-dev 46 | - cmake 47 | - env: SANITIZER=address 48 | script: ci/run_san.sh 49 | - env: SANITIZER=memory 50 | script: ci/run_san.sh 51 | - env: SANITIZER=thread 52 | script: ci/run_san.sh 53 | - env: TARGET=aarch64-unknown-linux-gnu 54 | - env: TARGET=aarch64-unknown-linux-gnu SYSV=1 55 | - env: TARGET=arm-unknown-linux-gnueabi 56 | - env: TARGET=arm-unknown-linux-musleabi 57 | - env: TARGET=armv7-unknown-linux-gnueabihf 58 | - env: TARGET=armv7-unknown-linux-musleabihf 59 | - env: TARGET=i586-unknown-linux-gnu 60 | - env: TARGET=i586-unknown-linux-gnu SYSV=1 61 | - env: TARGET=i686-unknown-linux-gnu 62 | - env: TARGET=i686-unknown-linux-gnu SYSV=1 63 | - env: TARGET=i686-unknown-linux-musl 64 | - env: TARGET=mips-unknown-linux-gnu 65 | - env: TARGET=mips64-unknown-linux-gnuabi64 66 | - env: TARGET=mips64el-unknown-linux-gnuabi64 67 | - env: TARGET=mipsel-unknown-linux-gnu 68 | - env: TARGET=powerpc-unknown-linux-gnu 69 | - env: TARGET=powerpc64-unknown-linux-gnu 70 | - env: TARGET=powerpc64-unknown-linux-gnu SYSV=1 71 | - env: TARGET=powerpc64le-unknown-linux-gnu 72 | - env: TARGET=sparc64-unknown-linux-gnu NOSTD=1 73 | - env: TARGET=x86_64-unknown-linux-gnu 74 | - env: TARGET=x86_64-unknown-linux-gnu SYSV=1 75 | - env: TARGET=x86_64-unknown-linux-gnu 76 | rust: stable 77 | - env: TARGET=x86_64-unknown-linux-musl 78 | - env: TARGET=x86_64-unknown-linux-musl SYSV=1 79 | - env: TARGET=aarch64-linux-android 80 | - env: TARGET=aarch64-linux-android SYSV=1 81 | - env: TARGET=arm-linux-androideabi 82 | - env: TARGET=armv7-linux-androideabi 83 | - env: TARGET=x86_64-linux-android 84 | - env: TARGET=i686-apple-darwin 85 | os: osx 86 | - env: TARGET=x86_64-apple-darwin 87 | os: osx 88 | osx_image: xcode9.2 89 | - env: TARGET=x86_64-apple-darwin SYSV=1 90 | os: osx 91 | osx_image: xcode9.2 92 | - env: TARGET=i386-apple-ios 93 | os: osx 94 | osx_image: xcode9.2 95 | - env: TARGET=x86_64-apple-ios 96 | os: osx 97 | osx_image: xcode9.2 98 | - env: TARGET=aarch64-apple-ios NORUN=1 99 | os: osx 100 | osx_image: xcode9.2 101 | - env: TARGET=armv7-apple-ios NORUN=1 102 | os: osx 103 | osx_image: xcode9.2 104 | - env: TARGET=i686-unknown-freebsd NORUN=1 105 | - env: TARGET=x86_64-unknown-freebsd NORUN=1 106 | - env: TARGET=x86_64-unknown-netbsd NORUN=1 107 | - env: TARGET=x86_64-sun-solaris NORUN=1 108 | 109 | script: 110 | - ci/run.sh 111 | 112 | env: 113 | global: 114 | - secure: i5Am1bhaK+6Zw/7b6uBxCPW+TYsPQ1EHaL8FMp0+KBeGtvpOIT1HsZd31C2gdvWouOVB/vjhS5qWa9YtkUw47Z7skiVRXdJfO+8ZUbH3ac8MUDc2uMrIRtEn37ncdn6IdsedD0MfqJc5BmGu9DUztHQ2oMokzEXGJINOdbadrZv8XUKNx+nlXOFXq6wQln8XhjBIFCP/JTkOr8zeuQdSH/TpaYMxsgfxwy4TSSQFRUmgUOevegcbjakeWvfd6pPiis0h275Ppykm9Jjkf7/RxL6C6uVgOZKcO7qVT/22PbrJwl5kucYelZr+gWXnxh65wyLXIy+t//6B1/3dhwuyr6U/LlxesvB8ZnS8Bt0cA4Loxh6yX/TTYTg734O56hjXRU6rlJW6RaaO6Vukv2s8kU+LhCSWijMtwRf7c2H0K1tksBT9wRZQZ06QxnR7P7jN48/QqAp1QZdT7jhXvOr8s7ROsJhbku0scUAOoNfTZ47SHjbMxGc7klI31BhaxFeaNsWAZMyIeigebhJ8gnZcPDO78J/6svBb83AgMLRPaO4l9y1png4iLdtXtkxr0S8F8pAf9VnDVSJBH6SN9/d7dRa1QwpNDpVcW52GEY5OQkVJ1Xkp9xbnW5nJpOXQIeJeix8VwsUXX289MUyPfOxDQhRCzeSExqks7P1veL+TNss= 115 | branches: 116 | only: 117 | - staging # bors r+ 118 | - trying # bors try 119 | - master 120 | notifications: 121 | email: 122 | on_success: never 123 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "libc" 7 | version = "0.2.107" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" 10 | 11 | [[package]] 12 | name = "mach2" 13 | version = "0.4.1" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8" 16 | dependencies = [ 17 | "libc", 18 | ] 19 | 20 | [[package]] 21 | name = "slice-ring-buffer" 22 | version = "0.3.4" 23 | dependencies = [ 24 | "libc", 25 | "mach2", 26 | "winapi", 27 | ] 28 | 29 | [[package]] 30 | name = "winapi" 31 | version = "0.3.9" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 34 | dependencies = [ 35 | "winapi-i686-pc-windows-gnu", 36 | "winapi-x86_64-pc-windows-gnu", 37 | ] 38 | 39 | [[package]] 40 | name = "winapi-i686-pc-windows-gnu" 41 | version = "0.4.0" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 44 | 45 | [[package]] 46 | name = "winapi-x86_64-pc-windows-gnu" 47 | version = "0.4.0" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 50 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | # This cargo file is used exclusively for development. It includes 2 | # dev-dependencies which causes 3 | [package] 4 | name = "slice-ring-buffer" 5 | version = "0.3.4" 6 | authors = ["Linus Probert "] 7 | description = "A double-ended queue that Deref's into a slice." 8 | documentation = "https://docs.rs/crate/slice-ring-buffer/" 9 | homepage = "https://github.com/liquidityc/slice_ring_buffer" 10 | repository = "https://github.com/liquidityc/slice_ring_buffer" 11 | readme = "README.md" 12 | keywords = ["collection", "deque", "ring", "circular", "buffer"] 13 | categories = ["data-structures", "no-std"] 14 | license = "MIT/Apache-2.0" 15 | edition = "2018" 16 | 17 | [badges] 18 | maintenance = { status = "passively-maintained" } 19 | 20 | [target.'cfg(all(any(target_os = "macos", target_os = "ios"), not(feature = "unix_sysv")))'.dependencies.mach2] 21 | version = "0.4.1" 22 | default-features = false 23 | 24 | [target.'cfg(target_os = "windows")'.dependencies.winapi] 25 | version = "0.3.*" 26 | features = ["memoryapi", "handleapi", "sysinfoapi", "winbase"] 27 | default-features = false 28 | 29 | [features] 30 | default = [ "use_std" ] 31 | 32 | # Enables features that require the standard library 33 | use_std = [ "libc/use_std" ] 34 | # Enables support for the bytes_buf trait. That is, 35 | # SliceDeque implements the Buf and BufMut traits. 36 | #bytes_buf = ["bytes", "use_std"] 37 | # Uses a super-portable but potentially slower System V interprocess 38 | # shared-memory implementation instead of system-specific ones on unix-like 39 | # targets. 40 | unix_sysv = [] 41 | 42 | #[dependencies] 43 | #bytes = { version = "0.4.*", optional = true } 44 | 45 | [target.'cfg(any(unix, target_os = "dragonfly"))'.dependencies.libc] 46 | version = "0.2" 47 | default-features = false 48 | 49 | [profile.dev] 50 | opt-level = 0 51 | debug = true 52 | lto = false 53 | debug-assertions = true 54 | codegen-units = 4 55 | incremental = true 56 | panic = 'unwind' 57 | 58 | [profile.bench] 59 | opt-level = 3 60 | debug = false 61 | rpath = false 62 | lto = true 63 | debug-assertions = false 64 | codegen-units = 1 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Slice Ring Buffer 2 | 3 | [![maintenance](https://img.shields.io/badge/maintenance-passively--maintained-orange)](https://img.shields.io/badge/maintenance-passively--maintained-orange) 4 | [![Rust](https://github.com/LiquidityC/slice_ring_buffer/actions/workflows/rust.yml/badge.svg)](https://github.com/LiquidityC/slice_ring_buffer/actions/workflows/rust.yml) 5 | [![Coverage Status](https://coveralls.io/repos/github/LiquidityC/slice_ring_buffer/badge.svg?branch=master)](https://coveralls.io/github/LiquidityC/slice_ring_buffer?branch=master) 6 | [![Security audit](https://github.com/LiquidityC/slice_ring_buffer/actions/workflows/audit-on-push.yml/badge.svg)](https://github.com/LiquidityC/slice_ring_buffer/actions/workflows/audit-on-push.yml) 7 | 8 | > A double-ended queue that `Deref`s into a slice, also known as a ring buffer or circular buffer. 9 | 10 | ## :warning: Notice :warning: 11 | 12 | ***This is a fork of [SliceDequeue](https://github.com/gnzlbg/slice_deque) that 13 | adds a security patch due to the original project no longer being maintained. 14 | I will keep the repo updated with security patches etc. But no active feature 15 | development will be done. Pull requests are welcome*** 16 | 17 | ## Advantages 18 | 19 | The main advantages of [`SliceRingBuffer`] are: 20 | 21 | * nicer API: since it `Deref`s to a slice, all operations that work on 22 | slices (like `sort`) "just work" for `SliceRingBuffer`. 23 | 24 | * efficient: as efficient as a slice (iteration, sorting, etc.), more efficient 25 | in general than `VecDeque`. 26 | 27 | ## Platform Support 28 | 29 | Windows, Linux, MacOS and every other unix-like OS is supported (although maybe 30 | untested). The following targets are known to work and pass all tests: 31 | 32 | ### Linux 33 | 34 | * aarch64-unknown-linux-gnu 35 | * arm-unknown-linux-gnueabi 36 | * arm-unknown-linux-musleabi 37 | * armv7-unknown-linux-gnueabihf 38 | * armv7-unknown-linux-musleabihf 39 | * i586-unknown-linux-gnu 40 | * i686-unknown-linux-gnu 41 | * i686-unknown-linux-musl 42 | * mips-unknown-linux-gnu 43 | * mips64-unknown-linux-gnuabi64 44 | * mips64el-unknown-linux-gnuabi64 45 | * mipsel-unknown-linux-gnu 46 | * powerpc-unknown-linux-gnu 47 | * powerpc64-unknown-linux-gnu 48 | * powerpc64le-unknown-linux-gnu 49 | * x86_64-unknown-linux-gnu 50 | * x86_64-unknown-linux-musl 51 | * aarch64-linux-android 52 | * arm-linux-androideabi 53 | * armv7-linux-androideabi 54 | * x86_64-linux-android 55 | 56 | ### MacOS X 57 | 58 | * i686-apple-darwin 59 | * x86_64-apple-darwin 60 | 61 | ### Windows 62 | 63 | * x86_64-pc-windows-msvc 64 | 65 | ## Drawbacks 66 | 67 | The main drawbacks of [`SliceRingBuffer`] are: 68 | 69 | * "constrained" platform support: the operating system must support virtual 70 | memory. In general, if you can use `std`, you can use [`SliceRingBuffer`]. 71 | 72 | * global allocator bypass: [`SliceRingBuffer`] bypasses Rust's global allocator / it 73 | is its own memory allocator, talking directly to the OS. That is, allocating 74 | and growing [`SliceRingBuffer`]s always involve system calls, while a [`VecDeque`] 75 | backed-up by a global allocator might receive memory owned by the allocator 76 | without any system calls at all. 77 | 78 | * smallest capacity constrained by the allocation granularity of the OS: some operating systems 79 | allow [`SliceRingBuffer`] to allocate memory in 4/8/64 kB chunks. 80 | 81 | When shouldn't you use it? In my opinion, if 82 | 83 | * you need to target `#[no_std]`, or 84 | * you can't use it (because your platform doesn't support it) 85 | 86 | you must use something else. If. 87 | 88 | * your ring-buffer's are very small, 89 | 90 | then by using [`SliceRingBuffer`] you might be trading memory for performance. Also, 91 | 92 | * your application has many short-lived ring-buffers, 93 | 94 | the cost of the system calls required to set up and grow the [`SliceRingBuffer`]s 95 | might not be amortized by your application (update: there is a pull-request open 96 | that caches allocations in thread-local heaps when the feature `use_std` is 97 | enabled significantly improving the performance of short-lived ring-buffers, but 98 | it has not been merged yet). Whether any of these trade-offs are worth it or not 99 | is application dependent, so don't take my word for it: measure. 100 | 101 | ## How it works 102 | 103 | The double-ended queue in the standard library ([`VecDeque`]) is implemented 104 | using a growable ring buffer (`0` represents uninitialized memory, and `T` 105 | represents one element in the queue): 106 | 107 | ```rust 108 | // [ 0 | 0 | 0 | T | T | T | 0 ] 109 | // ^:head ^:tail 110 | ``` 111 | 112 | When the queue grows beyond the end of the allocated buffer, its tail wraps 113 | around: 114 | 115 | ```rust 116 | // [ T | T | 0 | T | T | T | T ] 117 | // ^:tail ^:head 118 | ``` 119 | 120 | As a consequence, [`VecDeque`] cannot `Deref` into a slice, since its elements 121 | do not, in general, occupy a contiguous memory region. This complicates the 122 | implementation and its interface (for example, there is no `as_slice` method - 123 | the [`as_slices`] method returns a pair of slices) and has negative performance 124 | consequences (e.g. need to account for wrap around while iterating over the 125 | elements). 126 | 127 | This crates provides [`SliceRingBuffer`], a double-ended queue implemented with 128 | a growable *virtual* ring-buffer. 129 | 130 | A virtual ring-buffer implementation is very similar to the one used in 131 | `VecDeque`. The main difference is that a virtual ring-buffer maps two 132 | adjacent regions of virtual memory to the same region of physical memory: 133 | 134 | ```rust 135 | // Virtual memory: 136 | // 137 | // __________region_0_________ __________region_1_________ 138 | // [ 0 | 0 | 0 | T | T | T | 0 | 0 | 0 | 0 | T | T | T | 0 ] 139 | // ^:head ^:tail 140 | // 141 | // Physical memory: 142 | // 143 | // [ 0 | 0 | 0 | T | T | T | 0 ] 144 | // ^:head ^:tail 145 | ``` 146 | 147 | That is, both the virtual memory regions `0` and `1` above (top) map to the same 148 | physical memory (bottom). Just like `VecDeque`, when the queue grows beyond the 149 | end of the allocated physical memory region, the queue wraps around, and new 150 | elements continue to be appended at the beginning of the queue. However, because 151 | `SliceRingBuffer` maps the physical memory to two adjacent memory regions, in virtual 152 | memory space the queue maintais the ilusion of a contiguous memory layout: 153 | 154 | ```rust 155 | // Virtual memory: 156 | // 157 | // __________region_0_________ __________region_1_________ 158 | // [ T | T | 0 | T | T | T | T | T | T | 0 | T | T | T | T ] 159 | // ^:head ^:tail 160 | // 161 | // Physical memory: 162 | // 163 | // [ T | T | 0 | T | T | T | T ] 164 | // ^:tail ^:head 165 | ``` 166 | 167 | Since processes in many Operating Systems only deal with virtual memory 168 | addresses, leaving the mapping to physical memory to the CPU Memory Management 169 | Unit (MMU), [`SliceRingBuffer`] is able to `Deref`s into a slice in those systems. 170 | 171 | This simplifies [`SliceRingBuffer`]'s API and implementation, giving it a performance 172 | advantage over [`VecDeque`] in some situations. 173 | 174 | In general, you can think of [`SliceRingBuffer`] as a `Vec` with `O(1)` `pop_front` 175 | and amortized `O(1)` `push_front` methods. 176 | 177 | ## License 178 | 179 | This project is licensed under either of 180 | 181 | * Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) 182 | * MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT) 183 | 184 | at your option. 185 | 186 | ## Contribution 187 | 188 | Unless you explicitly state otherwise, any contribution intentionally submitted 189 | for inclusion in SliceRingBuffer by you, as defined in the Apache-2.0 license, shall be 190 | dual licensed as above, without any additional terms or conditions. 191 | 192 | [`VecDeque`]: https://doc.rust-lang.org/std/collections/struct.VecDeque.html 193 | [`as_slices`]: https://doc.rust-lang.org/std/collections/struct.VecDeque.html#method.as_slices 194 | [`SliceRingBuffer`]: struct.SliceRingBuffer.html 195 | 196 | [travis-shield]: https://img.shields.io/travis/gnzlbg/slice_deque.svg?style=flat-square 197 | [travis]: https://travis-ci.org/gnzlbg/slice_deque 198 | [appveyor-shield]: https://img.shields.io/appveyor/ci/gnzlbg/slice-deque.svg?style=flat-square 199 | [appveyor]: https://ci.appveyor.com/project/gnzlbg/slice-deque/branch/master 200 | [codecov-shield]: https://img.shields.io/codecov/c/github/gnzlbg/slice_deque.svg?style=flat-square 201 | [codecov]: https://codecov.io/gh/gnzlbg/slice_deque 202 | [docs-shield]: https://img.shields.io/badge/docs-online-blue.svg?style=flat-square 203 | [docs]: https://docs.rs/crate/slice-deque/ 204 | [license-shield]: https://img.shields.io/badge/License-MIT%2FApache2.0-green.svg?style=flat-square 205 | [license]: https://github.com/gnzlbg/slice_deque/blob/master/license.md 206 | [crate-shield]: https://img.shields.io/crates/v/slice_deque.svg?style=flat-square 207 | [crate]: https://crates.io/crates/slice_deque 208 | -------------------------------------------------------------------------------- /audit-on-push.yml: -------------------------------------------------------------------------------- 1 | name: Security audit 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - dev 7 | paths: 8 | - '**/Cargo.toml' 9 | - '**/Cargo.lock' 10 | jobs: 11 | security_audit: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v1 15 | - uses: actions-rs/audit-check@v1 16 | with: 17 | token: ${{ secrets.GITHUB_TOKEN }} 18 | -------------------------------------------------------------------------------- /benches/alloc.rs: -------------------------------------------------------------------------------- 1 | //! Tests the memory allocation performance 2 | #![feature(test)] 3 | 4 | extern crate slice_ring_buffer; 5 | extern crate test; 6 | 7 | use std::collections::VecDeque; 8 | 9 | /// Returns the `capacity` of a VecDeque that uses 10 | /// `factor` times allocation granularity memory. 11 | fn alloc_granularity_cap(factor: usize) -> usize { 12 | let mut deq = VecDeque::::with_capacity(1); 13 | // The capacity allocated is the allocation granularity of the OS (e.g. 1 14 | // memory page on OSX/Linux): 15 | let cap = deq.capacity(); 16 | let new_cap = factor * cap + 1; 17 | deq.resize(new_cap, 0); 18 | deq.capacity() 19 | } 20 | 21 | /// Allocates a VecDeque with capacity for `cap` elements, flushes it back 22 | /// to memory, and drops it. 23 | #[inline(always)] 24 | fn alloc_and_flush(cap: usize) { 25 | let mut deq = VecDeque::::with_capacity(cap); 26 | deq.push_back(1); 27 | test::black_box(&mut deq.get_mut(0)); 28 | test::black_box(&mut deq); 29 | } 30 | 31 | macro_rules! alloc { 32 | ($name:ident, $factor:expr) => { 33 | #[bench] 34 | fn $name(b: &mut test::Bencher) { 35 | let cap = alloc_granularity_cap($factor); 36 | b.iter(|| { 37 | alloc_and_flush(cap); 38 | }); 39 | } 40 | }; 41 | } 42 | 43 | alloc!(alloc_1, 1); 44 | alloc!(alloc_2, 2); 45 | alloc!(alloc_4, 4); 46 | alloc!(alloc_8, 8); 47 | alloc!(alloc_16, 16); 48 | alloc!(alloc_32, 32); 49 | alloc!(alloc_64, 64); 50 | alloc!(alloc_128, 128); 51 | alloc!(alloc_256, 256); 52 | alloc!(alloc_512, 512); 53 | alloc!(alloc_1024, 1024); 54 | alloc!(alloc_2048, 2048); 55 | alloc!(alloc_4096, 4096); // Linux 16 Mb 56 | alloc!(alloc_16384, 16384); // Linux 64 Mb 57 | alloc!(alloc_32768, 32768); // Linux 128 Mb 58 | alloc!(alloc_65536, 65536); // Linux 256 Mb 59 | alloc!(alloc_131072, 131072); // Linux 512 Mb 60 | -------------------------------------------------------------------------------- /benches/get.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | #![cfg_attr(feature = "cargo-clippy", allow(needless_range_loop))] 3 | 4 | extern crate slice_ring_buffer; 5 | extern crate test; 6 | 7 | use std::collections::VecDeque; 8 | 9 | const MAX_IDX: usize = 100_000; 10 | 11 | #[bench] 12 | fn get_contiguous_std_vecdeque(b: &mut test::Bencher) { 13 | let mut deq = VecDeque::::with_capacity(MAX_IDX); 14 | deq.resize(MAX_IDX, 3); 15 | b.iter(|| { 16 | for i in 0..MAX_IDX { 17 | test::black_box(&deq.get(i)); 18 | } 19 | }); 20 | } 21 | 22 | #[bench] 23 | fn get_contiguous_slice_ring_buffer(b: &mut test::Bencher) { 24 | let mut deq = 25 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_IDX); 26 | deq.resize(MAX_IDX, 3); 27 | b.iter(|| { 28 | for i in 0..MAX_IDX { 29 | test::black_box(&deq.get(i)); 30 | } 31 | }); 32 | } 33 | 34 | #[bench] 35 | fn get_contiguous_slice_ring_buffer_unchecked(b: &mut test::Bencher) { 36 | let mut deq = 37 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_IDX); 38 | deq.resize(MAX_IDX, 3); 39 | b.iter(|| { 40 | for i in 0..MAX_IDX { 41 | unsafe { 42 | test::black_box(&deq.get_unchecked(i)); 43 | } 44 | } 45 | }); 46 | } 47 | 48 | #[bench] 49 | fn get_chunked_std_vecdeque(b: &mut test::Bencher) { 50 | let mut deq = VecDeque::::with_capacity(MAX_IDX); 51 | deq.resize(MAX_IDX, 3); 52 | for _ in 0..MAX_IDX / 2 { 53 | deq.pop_front(); 54 | } 55 | for _ in 0..MAX_IDX / 4 { 56 | deq.push_back(3); 57 | } 58 | b.iter(|| { 59 | for i in 0..MAX_IDX / 4 * 3 { 60 | test::black_box(&deq.get(i)); 61 | } 62 | }); 63 | } 64 | 65 | #[bench] 66 | fn get_chunked_slice_ring_buffer(b: &mut test::Bencher) { 67 | let mut deq = 68 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_IDX); 69 | deq.resize(MAX_IDX, 3); 70 | for _ in 0..MAX_IDX / 2 { 71 | deq.pop_front(); 72 | } 73 | for _ in 0..MAX_IDX / 4 { 74 | deq.push_back(3); 75 | } 76 | b.iter(|| { 77 | for i in 0..MAX_IDX / 4 * 3 { 78 | test::black_box(&deq.get(i)); 79 | } 80 | }); 81 | } 82 | 83 | #[bench] 84 | fn get_chunked_slice_ring_buffer_unchecked(b: &mut test::Bencher) { 85 | let mut deq = 86 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_IDX); 87 | deq.resize(MAX_IDX, 3); 88 | for _ in 0..MAX_IDX / 2 { 89 | deq.pop_front(); 90 | } 91 | for _ in 0..MAX_IDX / 4 { 92 | deq.push_back(3); 93 | } 94 | b.iter(|| { 95 | for i in 0..MAX_IDX / 4 * 3 { 96 | unsafe { 97 | test::black_box(&deq.get_unchecked(i)); 98 | } 99 | } 100 | }); 101 | } 102 | -------------------------------------------------------------------------------- /benches/index.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | #![cfg_attr(feature = "cargo-clippy", allow(needless_range_loop))] 3 | 4 | extern crate slice_ring_buffer; 5 | extern crate test; 6 | 7 | use std::collections::VecDeque; 8 | 9 | const MAX_IDX: usize = 100_000; 10 | 11 | #[bench] 12 | fn index_contiguous_std_vecdeque(b: &mut test::Bencher) { 13 | let mut deq = VecDeque::::with_capacity(MAX_IDX); 14 | deq.resize(MAX_IDX, 3); 15 | b.iter(|| { 16 | for i in 0..MAX_IDX { 17 | test::black_box(&deq[i]); 18 | } 19 | }); 20 | } 21 | 22 | #[bench] 23 | fn index_contiguous_slice_ring_buffer(b: &mut test::Bencher) { 24 | let mut deq = 25 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_IDX); 26 | deq.resize(MAX_IDX, 3); 27 | b.iter(|| { 28 | for i in 0..MAX_IDX { 29 | test::black_box(&deq[i]); 30 | } 31 | }); 32 | } 33 | 34 | #[bench] 35 | fn index_chunked_std_vecdeque(b: &mut test::Bencher) { 36 | let mut deq = VecDeque::::with_capacity(MAX_IDX); 37 | deq.resize(MAX_IDX, 3); 38 | for _ in 0..MAX_IDX / 2 { 39 | deq.pop_front(); 40 | } 41 | for _ in 0..MAX_IDX / 4 { 42 | deq.push_back(3); 43 | } 44 | b.iter(|| { 45 | for i in 0..MAX_IDX / 4 * 3 { 46 | test::black_box(&deq[i]); 47 | } 48 | }); 49 | } 50 | 51 | #[bench] 52 | fn index_chunked_slice_ring_buffer(b: &mut test::Bencher) { 53 | let mut deq = 54 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_IDX); 55 | deq.resize(MAX_IDX, 3); 56 | for _ in 0..MAX_IDX / 2 { 57 | deq.pop_front(); 58 | } 59 | for _ in 0..MAX_IDX / 4 { 60 | deq.push_back(3); 61 | } 62 | b.iter(|| { 63 | for i in 0..MAX_IDX / 4 * 3 { 64 | test::black_box(&deq[i]); 65 | } 66 | }); 67 | } 68 | -------------------------------------------------------------------------------- /benches/insert.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | #![cfg_attr(feature = "cargo-clippy", allow(option_unwrap_used))] 3 | 4 | extern crate slice_ring_buffer; 5 | extern crate test; 6 | 7 | use std::collections::VecDeque; 8 | 9 | const MAX_NO_ITERS: usize = 1_000_000_000; 10 | 11 | #[bench] 12 | fn insert_front_vdeq(b: &mut test::Bencher) { 13 | let mut deq = VecDeque::::with_capacity(MAX_NO_ITERS); 14 | deq.resize(10, 3); 15 | b.iter(|| { 16 | test::black_box(deq.insert(0, 3)); 17 | test::black_box(&mut deq); 18 | }); 19 | } 20 | 21 | #[bench] 22 | fn insert_front_sdeq(b: &mut test::Bencher) { 23 | let mut deq = 24 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_NO_ITERS); 25 | deq.resize(10, 3); 26 | b.iter(|| { 27 | test::black_box(deq.insert(0, 3)); 28 | test::black_box(&mut deq); 29 | }); 30 | } 31 | 32 | #[bench] 33 | fn insert_back_vdeq(b: &mut test::Bencher) { 34 | let mut deq = VecDeque::::with_capacity(MAX_NO_ITERS); 35 | deq.resize(10, 3); 36 | b.iter(|| { 37 | let i = deq.len() - 1; 38 | test::black_box(deq.insert(i, 3)); 39 | test::black_box(&mut deq); 40 | }); 41 | } 42 | 43 | #[bench] 44 | fn insert_back_sdeq(b: &mut test::Bencher) { 45 | let mut deq = 46 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_NO_ITERS); 47 | deq.resize(10, 3); 48 | b.iter(|| { 49 | let i = deq.len() - 1; 50 | test::black_box(deq.insert(i, 3)); 51 | test::black_box(&mut deq); 52 | }); 53 | } 54 | 55 | #[bench] 56 | fn insert_mid_vdeq(b: &mut test::Bencher) { 57 | let mut deq = VecDeque::::with_capacity(MAX_NO_ITERS); 58 | deq.resize(10, 3); 59 | b.iter(|| { 60 | let i = (deq.len() - 1) / 2; 61 | test::black_box(deq.insert(i, 3)); 62 | test::black_box(&mut deq); 63 | }); 64 | } 65 | 66 | #[bench] 67 | fn insert_mid_sdeq(b: &mut test::Bencher) { 68 | let mut deq = 69 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_NO_ITERS); 70 | deq.resize(10, 3); 71 | b.iter(|| { 72 | let i = (deq.len() - 1) / 2; 73 | test::black_box(deq.insert(i, 3)); 74 | test::black_box(&mut deq); 75 | }); 76 | } 77 | 78 | #[bench] 79 | fn insert_quarter_vdeq(b: &mut test::Bencher) { 80 | let mut deq = VecDeque::::with_capacity(MAX_NO_ITERS); 81 | deq.resize(10, 3); 82 | b.iter(|| { 83 | let i = (deq.len() - 1) / 4; 84 | test::black_box(deq.insert(i, 3)); 85 | test::black_box(&mut deq); 86 | }); 87 | } 88 | 89 | #[bench] 90 | fn insert_quarter_sdeq(b: &mut test::Bencher) { 91 | let mut deq = 92 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_NO_ITERS); 93 | deq.resize(10, 3); 94 | b.iter(|| { 95 | let i = (deq.len() - 1) / 4; 96 | test::black_box(deq.insert(i, 3)); 97 | test::black_box(&mut deq); 98 | }); 99 | } 100 | 101 | #[bench] 102 | fn insert_three_quarter_vdeq(b: &mut test::Bencher) { 103 | let mut deq = VecDeque::::with_capacity(MAX_NO_ITERS); 104 | deq.resize(10, 3); 105 | b.iter(|| { 106 | let i = (deq.len() - 1) / 4 * 3; 107 | test::black_box(deq.insert(i, 3)); 108 | test::black_box(&mut deq); 109 | }); 110 | } 111 | 112 | #[bench] 113 | fn insert_three_quarter_sdeq(b: &mut test::Bencher) { 114 | let mut deq = 115 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_NO_ITERS); 116 | deq.resize(10, 3); 117 | b.iter(|| { 118 | let i = (deq.len() - 1) / 4 * 3; 119 | test::black_box(deq.insert(i, 3)); 120 | test::black_box(&mut deq); 121 | }); 122 | } 123 | -------------------------------------------------------------------------------- /benches/iter.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate slice_ring_buffer; 4 | extern crate test; 5 | 6 | use std::collections::VecDeque; 7 | 8 | const MAX_IDX: usize = 100_000; 9 | 10 | #[bench] 11 | fn iter_contiguous_std_vecdeque(b: &mut test::Bencher) { 12 | let mut deq = VecDeque::::with_capacity(MAX_IDX); 13 | deq.resize(MAX_IDX, 3); 14 | b.iter(|| { 15 | deq.iter().for_each(|v| { 16 | test::black_box(v); 17 | }); 18 | }); 19 | } 20 | 21 | #[bench] 22 | fn iter_contiguous_slice_ring_buffer(b: &mut test::Bencher) { 23 | let mut deq = 24 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_IDX); 25 | deq.resize(MAX_IDX, 3); 26 | b.iter(|| { 27 | deq.iter().for_each(|v| { 28 | test::black_box(v); 29 | }); 30 | }); 31 | } 32 | 33 | #[bench] 34 | fn iter_chunked_std_vecdeque(b: &mut test::Bencher) { 35 | let mut deq = VecDeque::::with_capacity(MAX_IDX); 36 | deq.resize(MAX_IDX, 3); 37 | for _ in 0..MAX_IDX / 2 { 38 | deq.pop_front(); 39 | } 40 | for _ in 0..MAX_IDX / 4 { 41 | deq.push_back(3); 42 | } 43 | b.iter(|| { 44 | deq.iter().for_each(|v| { 45 | test::black_box(v); 46 | }); 47 | }); 48 | } 49 | 50 | #[bench] 51 | fn iter_chunked_slice_ring_buffer(b: &mut test::Bencher) { 52 | let mut deq = 53 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_IDX); 54 | deq.resize(MAX_IDX, 3); 55 | for _ in 0..MAX_IDX / 2 { 56 | deq.pop_front(); 57 | } 58 | for _ in 0..MAX_IDX / 4 { 59 | deq.push_back(3); 60 | } 61 | b.iter(|| { 62 | deq.iter().for_each(|v| { 63 | test::black_box(v); 64 | }); 65 | }); 66 | } 67 | -------------------------------------------------------------------------------- /benches/pop_back.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | #![cfg_attr(feature = "cargo-clippy", allow(option_unwrap_used))] 3 | 4 | extern crate slice_ring_buffer; 5 | extern crate test; 6 | 7 | use std::collections::VecDeque; 8 | 9 | const MAX_NO_ITERS: usize = 1_000_000_000; 10 | 11 | #[bench] 12 | fn pop_back_std_vecdeque(b: &mut test::Bencher) { 13 | let mut deq = VecDeque::::with_capacity(MAX_NO_ITERS); 14 | deq.resize(MAX_NO_ITERS, 3); 15 | b.iter(|| { 16 | test::black_box(deq.pop_back().unwrap()); 17 | }); 18 | } 19 | 20 | #[bench] 21 | fn pop_back_slice_ring_buffer(b: &mut test::Bencher) { 22 | let mut deq = 23 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_NO_ITERS); 24 | deq.resize(MAX_NO_ITERS, 3); 25 | b.iter(|| { 26 | test::black_box(deq.pop_back().unwrap()); 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /benches/pop_front.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | #![cfg_attr(feature = "cargo-clippy", allow(option_unwrap_used))] 3 | 4 | extern crate slice_ring_buffer; 5 | extern crate test; 6 | 7 | use std::collections::VecDeque; 8 | 9 | const MAX_NO_ITERS: usize = 2_000_000_000; 10 | 11 | #[bench] 12 | fn pop_front_std_vecdeque(b: &mut test::Bencher) { 13 | let mut deq = VecDeque::::with_capacity(MAX_NO_ITERS); 14 | deq.resize(MAX_NO_ITERS, 3); 15 | b.iter(|| { 16 | test::black_box(deq.pop_front().unwrap()); 17 | }); 18 | } 19 | 20 | #[bench] 21 | fn pop_front_slice_ring_buffer(b: &mut test::Bencher) { 22 | let mut deq = 23 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_NO_ITERS); 24 | deq.resize(MAX_NO_ITERS, 3); 25 | b.iter(|| { 26 | test::black_box(deq.pop_front().unwrap()); 27 | }); 28 | } 29 | 30 | #[bench] 31 | fn truncate_front_1_slice_ring_buffer(b: &mut test::Bencher) { 32 | let mut deq = 33 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_NO_ITERS); 34 | deq.resize(MAX_NO_ITERS, 3); 35 | b.iter(|| { 36 | let new_len = deq.len() - 1; 37 | test::black_box(deq.truncate_front(new_len)); 38 | }); 39 | } 40 | 41 | #[bench] 42 | fn truncate_front_100_slice_ring_buffer(b: &mut test::Bencher) { 43 | let mut deq = 44 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_NO_ITERS); 45 | deq.resize(MAX_NO_ITERS, 3); 46 | b.iter(|| { 47 | let new_len = deq.len() - 100; 48 | test::black_box(deq.truncate_front(new_len)); 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /benches/push_back.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate slice_ring_buffer; 4 | extern crate test; 5 | 6 | use std::collections::VecDeque; 7 | 8 | const MAX_NO_ITERS: usize = 100_000_000; 9 | 10 | #[bench] 11 | fn push_back_std_vecdeque(b: &mut test::Bencher) { 12 | let mut deq = VecDeque::::with_capacity(MAX_NO_ITERS); 13 | b.iter(|| { 14 | deq.push_back(3); 15 | test::black_box(&mut deq); 16 | }); 17 | } 18 | 19 | #[bench] 20 | fn push_back_slice_ring_buffer(b: &mut test::Bencher) { 21 | let mut deq = 22 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_NO_ITERS); 23 | b.iter(|| { 24 | deq.push_back(3); 25 | test::black_box(&mut deq); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /benches/push_front.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate slice_ring_buffer; 4 | extern crate test; 5 | 6 | use std::collections::VecDeque; 7 | 8 | const MAX_NO_ITERS: usize = 1_000_000_000; 9 | 10 | #[bench] 11 | fn push_front_std_vecdeque(b: &mut test::Bencher) { 12 | let mut deq = VecDeque::::with_capacity(MAX_NO_ITERS); 13 | b.iter(|| { 14 | deq.push_front(3); 15 | test::black_box(&mut deq); 16 | }); 17 | } 18 | 19 | #[bench] 20 | fn push_front_slice_ring_buffer(b: &mut test::Bencher) { 21 | let mut deq = 22 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_NO_ITERS); 23 | b.iter(|| { 24 | deq.push_front(3); 25 | test::black_box(&mut deq); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /benches/remove.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | #![cfg_attr(feature = "cargo-clippy", allow(option_unwrap_used))] 3 | 4 | extern crate slice_ring_buffer; 5 | extern crate test; 6 | 7 | use std::collections::VecDeque; 8 | 9 | const MAX_NO_ITERS: usize = 1_000_000_000; 10 | 11 | #[bench] 12 | fn remove_front_vdeq(b: &mut test::Bencher) { 13 | let mut deq = VecDeque::::with_capacity(MAX_NO_ITERS); 14 | deq.resize(MAX_NO_ITERS, 3); 15 | b.iter(|| { 16 | test::black_box(deq.remove(0)); 17 | test::black_box(&mut deq); 18 | }); 19 | } 20 | 21 | #[bench] 22 | fn remove_front_sdeq(b: &mut test::Bencher) { 23 | let mut deq = 24 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_NO_ITERS); 25 | deq.resize(MAX_NO_ITERS, 3); 26 | b.iter(|| { 27 | test::black_box(deq.remove(0)); 28 | test::black_box(&mut deq); 29 | }); 30 | } 31 | 32 | #[bench] 33 | fn remove_back_vdeq(b: &mut test::Bencher) { 34 | let mut deq = VecDeque::::with_capacity(MAX_NO_ITERS); 35 | deq.resize(MAX_NO_ITERS, 3); 36 | b.iter(|| { 37 | let i = deq.len() - 1; 38 | test::black_box(deq.remove(i)); 39 | test::black_box(&mut deq); 40 | }); 41 | } 42 | 43 | #[bench] 44 | fn remove_back_sdeq(b: &mut test::Bencher) { 45 | let mut deq = 46 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_NO_ITERS); 47 | deq.resize(MAX_NO_ITERS, 3); 48 | b.iter(|| { 49 | let i = deq.len() - 1; 50 | test::black_box(deq.remove(i)); 51 | test::black_box(&mut deq); 52 | }); 53 | } 54 | 55 | #[bench] 56 | fn remove_mid_vdeq(b: &mut test::Bencher) { 57 | let mut deq = VecDeque::::with_capacity(MAX_NO_ITERS); 58 | deq.resize(MAX_NO_ITERS, 3); 59 | b.iter(|| { 60 | let i = (deq.len() - 1) / 2; 61 | test::black_box(deq.remove(i)); 62 | test::black_box(&mut deq); 63 | }); 64 | } 65 | 66 | #[bench] 67 | fn remove_mid_sdeq(b: &mut test::Bencher) { 68 | let mut deq = 69 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_NO_ITERS); 70 | deq.resize(MAX_NO_ITERS, 3); 71 | b.iter(|| { 72 | let i = (deq.len() - 1) / 2; 73 | test::black_box(deq.remove(i)); 74 | test::black_box(&mut deq); 75 | }); 76 | } 77 | 78 | #[bench] 79 | fn remove_quarter_vdeq(b: &mut test::Bencher) { 80 | let mut deq = VecDeque::::with_capacity(MAX_NO_ITERS); 81 | deq.resize(MAX_NO_ITERS, 3); 82 | b.iter(|| { 83 | let i = (deq.len() - 1) / 4; 84 | test::black_box(deq.remove(i)); 85 | test::black_box(&mut deq); 86 | }); 87 | } 88 | 89 | #[bench] 90 | fn remove_quarter_sdeq(b: &mut test::Bencher) { 91 | let mut deq = 92 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_NO_ITERS); 93 | deq.resize(MAX_NO_ITERS, 3); 94 | b.iter(|| { 95 | let i = (deq.len() - 1) / 4; 96 | test::black_box(deq.remove(i)); 97 | test::black_box(&mut deq); 98 | }); 99 | } 100 | 101 | #[bench] 102 | fn remove_three_quarter_vdeq(b: &mut test::Bencher) { 103 | let mut deq = VecDeque::::with_capacity(MAX_NO_ITERS); 104 | deq.resize(MAX_NO_ITERS, 3); 105 | b.iter(|| { 106 | let i = (deq.len() - 1) / 4 * 3; 107 | test::black_box(deq.remove(i)); 108 | test::black_box(&mut deq); 109 | }); 110 | } 111 | 112 | #[bench] 113 | fn remove_three_quarter_sdeq(b: &mut test::Bencher) { 114 | let mut deq = 115 | slice_ring_buffer::SliceRingBuffer::::with_capacity(MAX_NO_ITERS); 116 | deq.resize(MAX_NO_ITERS, 3); 117 | b.iter(|| { 118 | let i = (deq.len() - 1) / 4 * 3; 119 | test::black_box(deq.remove(i)); 120 | test::black_box(&mut deq); 121 | }); 122 | } 123 | -------------------------------------------------------------------------------- /bors.toml: -------------------------------------------------------------------------------- 1 | status = [ 2 | "continuous-integration/travis-ci/push" 3 | ] -------------------------------------------------------------------------------- /coverage.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | - dev 6 | pull_request: 7 | branches: 8 | - master 9 | - dev 10 | 11 | name: Code Coverage 12 | jobs: 13 | coverage: 14 | name: Run test coverage 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v1 18 | - name: Cache cargo registry 19 | uses: actions/cache@v1 20 | with: 21 | path: ~/.cargo/registry 22 | key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} 23 | - name: Cache cargo index 24 | uses: actions/cache@v1 25 | with: 26 | path: ~/.cargo/git 27 | key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} 28 | - name: Cache cargo binaries 29 | uses: actions/cache@v1 30 | with: 31 | path: ~/.cargo/bin 32 | key: ${{ runner.os }}-cargo-bin-${{ hashFiles('**/Cargo.lock') }} 33 | - name: Toolchain setup 34 | uses: actions-rs/toolchain@v1 35 | with: 36 | toolchain: nightly 37 | override: true 38 | components: llvm-tools-preview 39 | - name: Install grcov 40 | run: if [[ ! -e ~/.cargo/bin/grcov ]]; then cargo install grcov; fi 41 | - name: Updating package repo 42 | run: sudo apt-get update 43 | - name: Installing dependencies 44 | run: sudo apt-get install libasound2-dev 45 | - name: Run tests 46 | run: cargo test 47 | env: 48 | RUSTFLAGS: '-Zinstrument-coverage' 49 | LLVM_PROFILE_FILE: 'report-%p-%m.profraw' 50 | - name: Run grcov 51 | run: grcov . --binary-path target/debug/deps/ -s . -t lcov --branch --ignore-not-existing --ignore '../**' --ignore '/*' -o coverage.lcov 52 | - name: Coveralls upload 53 | uses: coverallsapp/github-action@master 54 | with: 55 | github-token: ${{ secrets.GITHUB_TOKEN }} 56 | path-to-lcov: coverage.lcov 57 | -------------------------------------------------------------------------------- /license-apache.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /license-mit.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Gonzalo Brito Gadeschi 2 | Copyright (c) 2017 The Rust Project Developers 3 | 4 | Permission is hereby granted, free of charge, to any 5 | person obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the 7 | Software without restriction, including without 8 | limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software 11 | is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice 15 | shall be included in all copies or substantial portions 16 | of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 19 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 20 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 21 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 22 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 25 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 | DEALINGS IN THE SOFTWARE. 27 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | This library is primarly distributed under the terms of both the MIT license and 2 | the Apache License (Version 2.0). 3 | 4 | See license-apache.md and license-mit.md for details. 5 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 79 2 | fn_params_layout = "Compressed" 3 | -------------------------------------------------------------------------------- /san/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "san" 3 | version = "0.1.0" 4 | authors = ["gnzlbg "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | slice-deque = { path = "..", features = ["unstable", "use_std"] } 9 | 10 | [profile.dev] 11 | panic = 'abort' 12 | 13 | [profile.release] 14 | panic = 'abort' 15 | -------------------------------------------------------------------------------- /san/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Tests that run under the sanitizers. 2 | #![allow(clippy::cognitive_complexity, clippy::option_unwrap_used)] 3 | 4 | #[macro_use] 5 | extern crate slice_deque; 6 | 7 | use slice_deque::SliceDeque; 8 | 9 | fn main() { 10 | // Single-threaded stable no-std asserts: 11 | { 12 | let mut deq: SliceDeque = SliceDeque::with_capacity(10); 13 | assert!(deq.capacity() >= 10); 14 | assert!(!deq.is_full()); 15 | assert!(deq.is_empty()); 16 | assert!(deq.len() == 0); 17 | deq.push_back(3); 18 | assert!(deq.len() == 1); 19 | assert!(!deq.is_full()); 20 | assert!(!deq.is_empty()); 21 | 22 | let mut deq2 = sdeq![4, 5, 6]; 23 | deq.append(&mut deq2); 24 | assert_eq!(deq, [3, 4, 5, 6]); 25 | assert_eq!(deq2, []); 26 | 27 | assert_eq!(deq.front(), Some(&3)); 28 | deq.push_front(3); 29 | assert_eq!(deq.front(), Some(&3)); 30 | (*deq.front_mut().unwrap()) = 2; 31 | assert_eq!(deq.front(), Some(&2)); 32 | 33 | assert_eq!(deq.back(), Some(&6)); 34 | deq.push_front(3); 35 | assert_eq!(deq.back(), Some(&6)); 36 | (*deq.back_mut().unwrap()) = 3; 37 | assert_eq!(deq.back(), Some(&3)); 38 | 39 | assert_eq!(deq.pop_front(), Some(3)); 40 | assert_eq!(deq.pop_front(), Some(2)); 41 | assert_eq!(deq.pop_front(), Some(3)); 42 | 43 | deq.push_back(7); 44 | assert_eq!(deq, [4, 5, 3, 7]); 45 | 46 | let cap = deq.capacity(); 47 | while cap == deq.capacity() { 48 | deq.push_back(1); 49 | } 50 | assert!(deq != [4, 5, 3, 7]); 51 | assert!(deq.capacity() > cap); 52 | while deq != [4, 5, 3, 7] { 53 | deq.pop_back(); 54 | } 55 | assert_eq!(deq, [4, 5, 3, 7]); 56 | 57 | deq.shrink_to_fit(); 58 | assert_eq!(deq.capacity(), cap); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | //! Macros and utilities. 2 | 3 | /// Small Ascii String. Used to write errors in `no_std` environments. 4 | pub struct TinyAsciiString { 5 | /// A buffer for the ascii string 6 | buf: [u8; 512], 7 | } 8 | 9 | impl TinyAsciiString { 10 | /// Creates a new string initialized to zero. 11 | pub fn new() -> Self { 12 | Self { buf: [0_u8; 512] } 13 | } 14 | /// Converts the Tiny Ascii String to an UTF-8 string (unchecked). 15 | pub unsafe fn as_str(&self) -> &str { 16 | crate::str::from_utf8_unchecked(&self.buf) 17 | } 18 | } 19 | 20 | impl crate::fmt::Write for TinyAsciiString { 21 | fn write_str(&mut self, s: &str) -> Result<(), crate::fmt::Error> { 22 | for (idx, b) in s.bytes().enumerate() { 23 | if let Some(v) = self.buf.get_mut(idx) { 24 | *v = b; 25 | } else { 26 | return Err(crate::fmt::Error); 27 | } 28 | } 29 | Ok(()) 30 | } 31 | } 32 | 33 | macro_rules! tiny_str { 34 | ($($t:tt)*) => ( 35 | { 36 | use crate::fmt::Write; 37 | let mut s: crate::macros::TinyAsciiString 38 | = crate::macros::TinyAsciiString::new(); 39 | write!(&mut s, $($t)*).unwrap(); 40 | s 41 | } 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /src/mirrored/buffer.rs: -------------------------------------------------------------------------------- 1 | //! Implements a mirrored memory buffer. 2 | 3 | use super::*; 4 | 5 | /// Number of required memory allocation units to hold `bytes`. 6 | fn no_required_allocation_units(bytes: usize) -> usize { 7 | let ag = allocation_granularity(); 8 | let r = ((bytes + ag - 1) / ag).max(1); 9 | let r = if r % 2 == 0 { r } else { r + 1 }; 10 | debug_assert!(r * ag >= bytes); 11 | debug_assert!(r % 2 == 0); 12 | r 13 | } 14 | 15 | /// Mirrored memory buffer of length `len`. 16 | /// 17 | /// The buffer elements in range `[0, len/2)` are mirrored into the range 18 | /// `[len/2, len)`. 19 | pub struct Buffer { 20 | /// Pointer to the first element in the buffer. 21 | ptr: NonNull, 22 | /// Length of the buffer: 23 | /// 24 | /// * it is NOT always a multiple of 2 25 | /// * the elements in range `[0, len/2)` are mirrored into the range 26 | /// `[len/2, len)`. 27 | len: usize, 28 | } 29 | 30 | impl Buffer { 31 | /// Number of elements in the buffer. 32 | pub fn len(&self) -> usize { 33 | self.len 34 | } 35 | 36 | /// Is the buffer empty? 37 | pub fn is_empty(&self) -> bool { 38 | self.len() == 0 39 | } 40 | 41 | /// Pointer to the first element in the buffer. 42 | pub unsafe fn ptr(&self) -> *mut T { 43 | self.ptr.as_ptr() 44 | } 45 | 46 | /// Interprets contents as a slice. 47 | /// 48 | /// Warning: Some memory might be uninitialized. 49 | pub unsafe fn as_slice(&self) -> &[T] { 50 | slice::from_raw_parts(self.ptr.as_ptr(), self.len()) 51 | } 52 | 53 | /// Interprets contents as a mut slice. 54 | /// 55 | /// Warning: Some memory might be uninitialized. 56 | pub unsafe fn as_mut_slice(&mut self) -> &mut [T] { 57 | slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len()) 58 | } 59 | 60 | /// Interprets content as a slice and access the `i`-th element. 61 | /// 62 | /// Warning: The memory of the `i`-th element might be uninitialized. 63 | pub unsafe fn get(&self, i: usize) -> &T { 64 | &self.as_slice()[i] 65 | } 66 | 67 | /// Interprets content as a mut slice and access the `i`-th element. 68 | /// 69 | /// Warning: The memory of the `i`-th element might be uninitialized. 70 | pub unsafe fn get_mut(&mut self, i: usize) -> &mut T { 71 | &mut self.as_mut_slice()[i] 72 | } 73 | 74 | fn empty_len() -> usize { 75 | if mem::size_of::() == 0 { 76 | isize::max_value() as usize * 2 77 | } else { 78 | 0 79 | } 80 | } 81 | 82 | /// Creates a new empty `Buffer`. 83 | pub fn new() -> Self { 84 | // Here `ptr` is initialized to a magic value but `len == 0` 85 | // will ensure that it is never dereferenced in this state. 86 | Self { 87 | ptr: NonNull::dangling(), 88 | len: Self::empty_len(), 89 | } 90 | } 91 | 92 | /// Creates a new empty `Buffer` from a `ptr` and a `len`. 93 | /// 94 | /// # Panics 95 | /// 96 | /// If `ptr` is null. 97 | pub unsafe fn from_raw_parts(ptr: *mut T, len: usize) -> Self { 98 | assert!(len % 2 == 0); 99 | assert!(!ptr.is_null()); 100 | if mem::size_of::() == 0 { 101 | debug_assert_eq!(len, Self::empty_len()); 102 | } 103 | Self { 104 | ptr: NonNull::new_unchecked(ptr), 105 | len, 106 | } 107 | } 108 | 109 | /// Total number of bytes in the buffer. 110 | pub fn size_in_bytes(len: usize) -> usize { 111 | let v = no_required_allocation_units(len * mem::size_of::()) 112 | * allocation_granularity(); 113 | debug_assert!( 114 | v >= len * mem::size_of::(), 115 | "len: {}, so: {}, v: {}", 116 | len, 117 | mem::size_of::(), 118 | v 119 | ); 120 | v 121 | } 122 | 123 | /// Create a mirrored buffer containing `len` `T`s where the first half of 124 | /// the buffer is mirrored into the second half. 125 | pub fn uninitialized(len: usize) -> Result { 126 | // Zero-sized types: 127 | if mem::size_of::() == 0 { 128 | return Ok(Self { 129 | ptr: NonNull::dangling(), 130 | len: Self::empty_len(), 131 | }); 132 | } 133 | // The alignment requirements of `T` must be smaller than the 134 | // allocation granularity. 135 | assert!(mem::align_of::() <= allocation_granularity()); 136 | // To split the buffer in two halfs the number of elements must be a 137 | // multiple of two, and greater than zero to be able to mirror 138 | // something. 139 | if len == 0 { 140 | return Ok(Self::new()); 141 | } 142 | assert!(len % 2 == 0); 143 | 144 | // How much memory we need: 145 | let alloc_size = Self::size_in_bytes(len); 146 | debug_assert!(alloc_size > 0); 147 | debug_assert!(alloc_size % 2 == 0); 148 | debug_assert!(alloc_size % allocation_granularity() == 0); 149 | debug_assert!(alloc_size >= len * mem::size_of::()); 150 | 151 | let ptr = allocate_mirrored(alloc_size)?; 152 | Ok(Self { 153 | ptr: unsafe { NonNull::new_unchecked(ptr as *mut T) }, 154 | len: alloc_size / mem::size_of::(), 155 | // Note: len is not a multiple of two: debug_assert!(len % 2 == 0); 156 | }) 157 | } 158 | } 159 | 160 | impl Drop for Buffer { 161 | fn drop(&mut self) { 162 | if mem::size_of::() == 0 { 163 | debug_assert_eq!(self.len, Self::empty_len()); 164 | return; 165 | } 166 | if self.is_empty() { 167 | return; 168 | } 169 | 170 | let buffer_size_in_bytes = Self::size_in_bytes(self.len()); 171 | let first_half_ptr = self.ptr.as_ptr() as *mut u8; 172 | unsafe { deallocate_mirrored(first_half_ptr, buffer_size_in_bytes) }; 173 | } 174 | } 175 | 176 | impl Clone for Buffer 177 | where 178 | T: Clone, 179 | { 180 | fn clone(&self) -> Self { 181 | unsafe { 182 | let mid = self.len() / 2; 183 | let mut c = Self::uninitialized(self.len()) 184 | .expect("allocating a new mirrored buffer failed"); 185 | let (from, _) = self.as_slice().split_at(mid); 186 | { 187 | let (to, _) = c.as_mut_slice().split_at_mut(mid); 188 | to[..mid].clone_from_slice(&from[..mid]); 189 | } 190 | c 191 | } 192 | } 193 | } 194 | 195 | impl Default for Buffer { 196 | fn default() -> Self { 197 | Self::new() 198 | } 199 | } 200 | 201 | // Safe because it is possible to free this from a different thread 202 | unsafe impl Send for Buffer where T: Send {} 203 | // Safe because this doesn't use any kind of interior mutability. 204 | unsafe impl Sync for Buffer where T: Sync {} 205 | 206 | #[cfg(test)] 207 | mod tests { 208 | use super::*; 209 | 210 | fn is_send_sync() -> bool 211 | where 212 | T: Send + Sync, 213 | { 214 | true 215 | } 216 | 217 | #[test] 218 | fn buffer_send_sync() { 219 | assert!(is_send_sync::>()); 220 | } 221 | 222 | #[test] 223 | fn test_new() { 224 | let a = Buffer::::new(); 225 | assert!(a.is_empty()); 226 | } 227 | 228 | fn test_alloc(size: usize) { 229 | unsafe { 230 | let mut a = Buffer::::uninitialized(size).unwrap(); 231 | let sz = a.len(); 232 | assert!(sz >= size); 233 | assert_eq!( 234 | sz, 235 | Buffer::::size_in_bytes(size) / mem::size_of::() 236 | ); 237 | 238 | for i in 0..sz / 2 { 239 | *a.get_mut(i) = i as u64; 240 | } 241 | 242 | let (first_half_mut, second_half_mut) = 243 | a.as_mut_slice().split_at_mut(sz / 2); 244 | 245 | let mut c = 0; 246 | for (i, j) in first_half_mut.iter().zip(second_half_mut) { 247 | assert_eq!(i, j); 248 | c += 1; 249 | } 250 | assert_eq!(c, sz / 2); 251 | } 252 | } 253 | 254 | #[test] 255 | fn allocations() { 256 | let elements_per_alloc_unit = 257 | allocation_granularity() / mem::size_of::(); 258 | let sizes = [ 259 | 8, 260 | elements_per_alloc_unit / 2, 261 | elements_per_alloc_unit, 262 | elements_per_alloc_unit * 4, 263 | ]; 264 | for &i in &sizes { 265 | test_alloc(i); 266 | } 267 | } 268 | 269 | #[test] 270 | fn no_alloc_units_required() { 271 | // Up to the allocation unit size we always need two allocation units 272 | assert_eq!( 273 | no_required_allocation_units(allocation_granularity() / 4), 274 | 2 275 | ); 276 | assert_eq!( 277 | no_required_allocation_units(allocation_granularity() / 2), 278 | 2 279 | ); 280 | assert_eq!(no_required_allocation_units(allocation_granularity()), 2); 281 | // For sizes larger than the allocation units we always round up to the 282 | // next even number of allocation units: 283 | assert_eq!( 284 | no_required_allocation_units(allocation_granularity() + 1), 285 | 2 286 | ); 287 | assert_eq!( 288 | no_required_allocation_units(2 * allocation_granularity()), 289 | 2 290 | ); 291 | assert_eq!( 292 | no_required_allocation_units(3 * allocation_granularity()), 293 | 4 294 | ); 295 | assert_eq!( 296 | no_required_allocation_units(4 * allocation_granularity()), 297 | 4 298 | ); 299 | assert_eq!( 300 | no_required_allocation_units(5 * allocation_granularity()), 301 | 6 302 | ); 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /src/mirrored/linux.rs: -------------------------------------------------------------------------------- 1 | //! Non-racy linux-specific mirrored memory allocation. 2 | use libc::{ 3 | c_char, c_int, c_long, c_uint, c_void, close, ftruncate, mkstemp, mmap, 4 | munmap, off_t, size_t, sysconf, unlink, ENOSYS, MAP_FAILED, MAP_FIXED, 5 | MAP_SHARED, PROT_READ, PROT_WRITE, _SC_PAGESIZE, 6 | }; 7 | 8 | #[cfg(any(target_os = "android", target_os = "openbsd"))] 9 | use libc::__errno; 10 | #[cfg(not(any(target_os = "android", target_os = "openbsd")))] 11 | use libc::__errno_location; 12 | 13 | use super::{ptr, AllocError}; 14 | 15 | /// [`memfd_create`] - create an anonymous file 16 | /// 17 | /// [`memfd_create`]: http://man7.org/linux/man-pages/man2/memfd_create.2.html 18 | #[cfg(not(target_os = "openbsd"))] 19 | fn memfd_create(name: *const c_char, flags: c_uint) -> c_long { 20 | use libc::{syscall, SYS_memfd_create}; 21 | 22 | unsafe { syscall(SYS_memfd_create, name, flags) } 23 | } 24 | 25 | #[cfg(target_os = "openbsd")] 26 | fn memfd_create(_name: *mut c_char, _flags: c_uint) -> c_long { 27 | unsafe { *__errno() = ENOSYS }; 28 | return -1; 29 | } 30 | 31 | /// Returns the size of a memory allocation unit. 32 | /// 33 | /// In Linux-like systems this equals the page-size. 34 | pub fn allocation_granularity() -> usize { 35 | unsafe { sysconf(_SC_PAGESIZE) as usize } 36 | } 37 | 38 | /// Reads `errno`. 39 | fn errno() -> c_int { 40 | #[cfg(not(any(target_os = "android", target_os = "openbsd")))] 41 | unsafe { 42 | *__errno_location() 43 | } 44 | #[cfg(any(target_os = "android", target_os = "openbsd"))] 45 | unsafe { 46 | *__errno() 47 | } 48 | } 49 | 50 | /// Allocates an uninitialzied buffer that holds `size` bytes, where 51 | /// the bytes in range `[0, size / 2)` are mirrored into the bytes in 52 | /// range `[size / 2, size)`. 53 | /// 54 | /// On Linux the algorithm is as follows: 55 | /// 56 | /// * 1. Allocate a memory-mapped file containing `size / 2` bytes. 57 | /// * 2. Map the file into `size` bytes of virtual memory. 58 | /// * 3. Map the file into the last `size / 2` bytes of the virtual memory 59 | /// region obtained in step 2. 60 | /// 61 | /// This algorithm doesn't have any races. 62 | /// 63 | /// # Panics 64 | /// 65 | /// If `size` is zero or `size / 2` is not a multiple of the 66 | /// allocation granularity. 67 | pub fn allocate_mirrored(size: usize) -> Result<*mut u8, AllocError> { 68 | unsafe { 69 | let half_size = size / 2; 70 | assert!(size != 0); 71 | assert!(half_size % allocation_granularity() == 0); 72 | 73 | // create temporary file 74 | let mut fname = *b"/tmp/slice_deque_fileXXXXXX\0"; 75 | let mut fd: c_long = 76 | memfd_create(fname.as_mut_ptr() as *mut c_char, 0); 77 | if fd == -1 && errno() == ENOSYS { 78 | // memfd_create is not implemented, use mkstemp instead: 79 | fd = c_long::from(mkstemp(fname.as_mut_ptr() as *mut c_char)); 80 | // and unlink the file 81 | if fd != -1 { 82 | unlink(fname.as_mut_ptr() as *mut c_char); 83 | } 84 | } 85 | if fd == -1 { 86 | print_error("memfd_create failed"); 87 | return Err(AllocError::Other); 88 | } 89 | let fd = fd as c_int; 90 | if ftruncate(fd, half_size as off_t) == -1 { 91 | print_error("ftruncate failed"); 92 | if close(fd) == -1 { 93 | print_error("@ftruncate: close failed"); 94 | } 95 | return Err(AllocError::Oom); 96 | }; 97 | 98 | // mmap memory 99 | let ptr = mmap( 100 | ptr::null_mut(), 101 | size, 102 | PROT_READ | PROT_WRITE, 103 | MAP_SHARED, 104 | fd, 105 | 0, 106 | ); 107 | if ptr == MAP_FAILED { 108 | print_error("@first: mmap failed"); 109 | if close(fd) == -1 { 110 | print_error("@first: close failed"); 111 | } 112 | return Err(AllocError::Oom); 113 | } 114 | 115 | let ptr2 = mmap( 116 | (ptr as *mut u8).offset(half_size as isize) as *mut c_void, 117 | half_size, 118 | PROT_READ | PROT_WRITE, 119 | MAP_SHARED | MAP_FIXED, 120 | fd, 121 | 0, 122 | ); 123 | if ptr2 == MAP_FAILED { 124 | print_error("@second: mmap failed"); 125 | if munmap(ptr, size as size_t) == -1 { 126 | print_error("@second: munmap failed"); 127 | } 128 | if close(fd) == -1 { 129 | print_error("@second: close failed"); 130 | } 131 | return Err(AllocError::Other); 132 | } 133 | 134 | if close(fd) == -1 { 135 | print_error("@success: close failed"); 136 | } 137 | Ok(ptr as *mut u8) 138 | } 139 | } 140 | 141 | /// Deallocates the mirrored memory region at `ptr` of `size` bytes. 142 | /// 143 | /// # Unsafe 144 | /// 145 | /// `ptr` must have been obtained from a call to `allocate_mirrored(size)`, 146 | /// otherwise the behavior is undefined. 147 | /// 148 | /// # Panics 149 | /// 150 | /// If `size` is zero or `size / 2` is not a multiple of the 151 | /// allocation granularity, or `ptr` is null. 152 | pub unsafe fn deallocate_mirrored(ptr: *mut u8, size: usize) { 153 | assert!(!ptr.is_null()); 154 | assert!(size != 0); 155 | assert!(size % allocation_granularity() == 0); 156 | if munmap(ptr as *mut c_void, size as size_t) == -1 { 157 | print_error("deallocate munmap failed"); 158 | } 159 | } 160 | 161 | /// Prints last os error at `location`. 162 | #[cfg(all(debug_assertions, feature = "use_std"))] 163 | fn print_error(location: &str) { 164 | eprintln!( 165 | "Error at {}: {}", 166 | location, 167 | ::std::io::Error::last_os_error() 168 | ); 169 | } 170 | 171 | /// Prints last os error at `location`. 172 | #[cfg(not(all(debug_assertions, feature = "use_std")))] 173 | fn print_error(_location: &str) {} 174 | -------------------------------------------------------------------------------- /src/mirrored/macos.rs: -------------------------------------------------------------------------------- 1 | //! Implements the allocator hooks on top of mach. 2 | 3 | #![allow(clippy::shadow_unrelated)] 4 | 5 | use super::mem; 6 | 7 | use mach::boolean::boolean_t; 8 | use mach::kern_return::*; 9 | use mach::mach_types::mem_entry_name_port_t; 10 | use mach::memory_object_types::{ 11 | memory_object_offset_t, memory_object_size_t, 12 | }; 13 | use mach::traps::mach_task_self; 14 | use mach::vm::{ 15 | mach_make_memory_entry_64, mach_vm_allocate, mach_vm_deallocate, 16 | mach_vm_remap, 17 | }; 18 | use mach::vm_inherit::VM_INHERIT_NONE; 19 | use mach::vm_prot::{vm_prot_t, VM_PROT_READ, VM_PROT_WRITE}; 20 | use mach::vm_statistics::{VM_FLAGS_ANYWHERE, VM_FLAGS_FIXED}; 21 | use mach::vm_types::mach_vm_address_t; 22 | use mach2 as mach; 23 | 24 | use super::AllocError; 25 | 26 | /// TODO: not exposed by the mach crate 27 | const VM_FLAGS_OVERWRITE: ::libc::c_int = 0x4000_i32; 28 | 29 | /// Returns the size of an allocation unit. 30 | /// 31 | /// In `MacOSX` this equals the page size. 32 | pub fn allocation_granularity() -> usize { 33 | unsafe { mach::vm_page_size::vm_page_size as usize } 34 | } 35 | 36 | /// Allocates an uninitialzied buffer that holds `size` bytes, where 37 | /// the bytes in range `[0, size / 2)` are mirrored into the bytes in 38 | /// range `[size / 2, size)`. 39 | /// 40 | /// On Macos X the algorithm is as follows: 41 | /// 42 | /// * 1. Allocate twice the memory (`size` bytes) 43 | /// * 2. Deallocate the second half (bytes in range `[size / 2, 0)`) 44 | /// * 3. Race condition: mirror bytes of the first half into the second 45 | /// half. 46 | /// 47 | /// If we get a race (e.g. because some other process allocates to the 48 | /// second half) we release all the resources (we need to deallocate the 49 | /// memory) and try again (up to a maximum of `MAX_NO_ALLOC_ITERS` times). 50 | /// 51 | /// # Panics 52 | /// 53 | /// If `size` is zero or `size / 2` is not a multiple of the 54 | /// allocation granularity. 55 | pub fn allocate_mirrored(size: usize) -> Result<*mut u8, AllocError> { 56 | unsafe { 57 | assert!(size != 0); 58 | let half_size = size / 2; 59 | assert!(half_size % allocation_granularity() == 0); 60 | 61 | let task = mach_task_self(); 62 | 63 | // Allocate memory to hold the whole buffer: 64 | let mut addr: mach_vm_address_t = 0; 65 | let r: kern_return_t = mach_vm_allocate( 66 | task, 67 | &mut addr as *mut mach_vm_address_t, 68 | size as u64, 69 | VM_FLAGS_ANYWHERE, 70 | ); 71 | if r != KERN_SUCCESS { 72 | // If the first allocation fails, there is nothing to 73 | // deallocate and we can just fail to allocate: 74 | print_error("initial alloc", r); 75 | return Err(AllocError::Oom); 76 | } 77 | debug_assert!(addr != 0); 78 | 79 | // Set the size of the first half to size/2: 80 | let r: kern_return_t = mach_vm_allocate( 81 | task, 82 | &mut addr as *mut mach_vm_address_t, 83 | half_size as u64, 84 | VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, 85 | ); 86 | if r != KERN_SUCCESS { 87 | // If the first allocation fails, there is nothing to 88 | // deallocate and we can just fail to allocate: 89 | print_error("first half alloc", r); 90 | return Err(AllocError::Other); 91 | } 92 | 93 | // Get an object handle to the first memory region: 94 | let mut memory_object_size = half_size as memory_object_size_t; 95 | let mut object_handle = 96 | mem::MaybeUninit::::uninit(); 97 | let parent_handle: mem_entry_name_port_t = 0; 98 | let r: kern_return_t = mach_make_memory_entry_64( 99 | task, 100 | &mut memory_object_size as *mut memory_object_size_t, 101 | addr as memory_object_offset_t, 102 | VM_PROT_READ | VM_PROT_WRITE, 103 | object_handle.as_mut_ptr(), 104 | parent_handle, 105 | ); 106 | 107 | if r != KERN_SUCCESS { 108 | // If making the memory entry fails we should deallocate the first 109 | // allocation: 110 | print_error("make memory entry", r); 111 | if dealloc(addr as *mut u8, size).is_err() { 112 | panic!("failed to deallocate after error"); 113 | } 114 | return Err(AllocError::Other); 115 | } 116 | 117 | // Map the first half to the second half using the object handle: 118 | let mut to = (addr as *mut u8).add(half_size) as mach_vm_address_t; 119 | let mut current_prot = mem::MaybeUninit::::uninit(); 120 | let mut out_prot = mem::MaybeUninit::::uninit(); 121 | let r: kern_return_t = mach_vm_remap( 122 | task, 123 | &mut to as *mut mach_vm_address_t, 124 | half_size as u64, 125 | /* mask: */ 0, 126 | VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, 127 | task, 128 | addr, 129 | /* copy: */ 0 as boolean_t, 130 | current_prot.as_mut_ptr(), 131 | out_prot.as_mut_ptr(), 132 | VM_INHERIT_NONE, 133 | ); 134 | 135 | if r != KERN_SUCCESS { 136 | print_error("map first to second half", r); 137 | // If making the memory entry fails we deallocate all the memory 138 | if dealloc(addr as *mut u8, size).is_err() { 139 | panic!("failed to deallocate after error"); 140 | } 141 | return Err(AllocError::Other); 142 | } 143 | 144 | // TODO: object_handle is leaked here. Investigate whether this is ok. 145 | 146 | Ok(addr as *mut u8) 147 | } 148 | } 149 | 150 | /// Deallocates the mirrored memory region at `ptr` of `size` bytes. 151 | /// 152 | /// # Unsafe 153 | /// 154 | /// `ptr` must have been obtained from a call to `allocate_mirrored(size)`, 155 | /// otherwise the behavior is undefined. 156 | /// 157 | /// # Panics 158 | /// 159 | /// If `size` is zero or `size / 2` is not a multiple of the 160 | /// allocation granularity, or `ptr` is null. 161 | pub unsafe fn deallocate_mirrored(ptr: *mut u8, size: usize) { 162 | assert!(!ptr.is_null()); 163 | assert!(size != 0); 164 | assert!(size % allocation_granularity() == 0); 165 | dealloc(ptr, size).expect("deallocating mirrored buffer failed"); 166 | } 167 | 168 | /// Tries to deallocates `size` bytes of memory starting at `ptr`. 169 | /// 170 | /// # Unsafety 171 | /// 172 | /// The `ptr` must have been obtained from a previous call to `alloc` and point 173 | /// to a memory region containing at least `size` bytes. 174 | /// 175 | /// # Panics 176 | /// 177 | /// If `size` is zero or not a multiple of the `allocation_granularity`, or if 178 | /// `ptr` is null. 179 | unsafe fn dealloc(ptr: *mut u8, size: usize) -> Result<(), ()> { 180 | assert!(size != 0); 181 | assert!(size % allocation_granularity() == 0); 182 | assert!(!ptr.is_null()); 183 | let addr = ptr as mach_vm_address_t; 184 | let r: kern_return_t = 185 | mach_vm_deallocate(mach_task_self(), addr, size as u64); 186 | if r != KERN_SUCCESS { 187 | print_error("dealloc", r); 188 | return Err(()); 189 | } 190 | Ok(()) 191 | } 192 | 193 | /// Prints last os error at `location`. 194 | #[cfg(not(all(debug_assertions, feature = "use_std")))] 195 | fn print_error(_msg: &str, _code: kern_return_t) {} 196 | 197 | /// Prints last os error at `location`. 198 | #[cfg(all(debug_assertions, feature = "use_std"))] 199 | fn print_error(msg: &str, code: kern_return_t) { 200 | eprintln!("ERROR at \"{}\": {}", msg, report_error(code)); 201 | } 202 | 203 | /// Maps a vm `kern_return_t` to an error string. 204 | #[cfg(all(debug_assertions, feature = "use_std"))] 205 | fn report_error(error: kern_return_t) -> &'static str { 206 | use mach::kern_return::*; 207 | match error { 208 | KERN_ABORTED => "KERN_ABORTED", 209 | KERN_ALREADY_IN_SET => "KERN_ALREADY_IN_SET", 210 | KERN_ALREADY_WAITING => "KERN_ALREADY_WAITING", 211 | KERN_CODESIGN_ERROR => "KERN_CODESIGN_ERROR", 212 | KERN_DEFAULT_SET => "KERN_DEFAULT_SET", 213 | KERN_EXCEPTION_PROTECTED => "KERN_EXCEPTION_PROTECTED", 214 | KERN_FAILURE => "KERN_FAILURE", 215 | KERN_INVALID_ADDRESS => "KERN_INVALID_ADDRESS", 216 | KERN_INVALID_ARGUMENT => "KERN_INVALID_ARGUMENT", 217 | KERN_INVALID_CAPABILITY => "KERN_INVALID_CAPABILITY", 218 | KERN_INVALID_HOST => "KERN_INVALID_HOST", 219 | KERN_INVALID_LEDGER => "KERN_INVALID_LEDGER", 220 | KERN_INVALID_MEMORY_CONTROL => "KERN_INVALID_MEMORY_CONTROL", 221 | KERN_INVALID_NAME => "KERN_INVALID_NAME", 222 | KERN_INVALID_OBJECT => "KERN_INVALID_OBJECT", 223 | KERN_INVALID_POLICY => "KERN_INVALID_POLICY", 224 | KERN_INVALID_PROCESSOR_SET => "KERN_INVALID_PROCESSOR_SET", 225 | KERN_INVALID_RIGHT => "KERN_INVALID_RIGHT", 226 | KERN_INVALID_SECURITY => "KERN_INVALID_SECURITY", 227 | KERN_INVALID_TASK => "KERN_INVALID_TASK", 228 | KERN_INVALID_VALUE => "KERN_INVALID_VALUE", 229 | KERN_LOCK_OWNED => "KERN_LOCK_OWNED", 230 | KERN_LOCK_OWNED_SELF => "KERN_LOCK_OWNED_SELF", 231 | KERN_LOCK_SET_DESTROYED => "KERN_LOCK_SET_DESTROYED", 232 | KERN_LOCK_UNSTABLE => "KERN_LOCK_UNSTABLE", 233 | KERN_MEMORY_DATA_MOVED => "KERN_MEMORY_DATA_MOVED", 234 | KERN_MEMORY_ERROR => "KERN_MEMORY_ERROR", 235 | KERN_MEMORY_FAILURE => "KERN_MEMORY_FAILURE", 236 | KERN_MEMORY_PRESENT => "KERN_MEMORY_PRESENT", 237 | KERN_MEMORY_RESTART_COPY => "KERN_MEMORY_RESTART_COPY", 238 | KERN_NAME_EXISTS => "KERN_NAME_EXISTS", 239 | KERN_NODE_DOWN => "KERN_NODE_DOWN", 240 | KERN_NOT_DEPRESSED => "KERN_NOT_DEPRESSED", 241 | KERN_NOT_IN_SET => "KERN_NOT_IN_SET", 242 | KERN_NOT_RECEIVER => "KERN_NOT_RECEIVER", 243 | KERN_NOT_SUPPORTED => "KERN_NOT_SUPPORTED", 244 | KERN_NOT_WAITING => "KERN_NOT_WAITING", 245 | KERN_NO_ACCESS => "KERN_NO_ACCESS", 246 | KERN_NO_SPACE => "KERN_NO_SPACE", 247 | KERN_OPERATION_TIMED_OUT => "KERN_OPERATION_TIMED_OUT", 248 | KERN_POLICY_LIMIT => "KERN_POLICY_LIMIT", 249 | KERN_POLICY_STATIC => "KERN_POLICY_STATIC", 250 | KERN_PROTECTION_FAILURE => "KERN_PROTECTION_FAILURE", 251 | KERN_RESOURCE_SHORTAGE => "KERN_RESOURCE_SHORTAGE", 252 | KERN_RETURN_MAX => "KERN_RETURN_MAX", 253 | KERN_RIGHT_EXISTS => "KERN_RIGHT_EXISTS", 254 | KERN_RPC_CONTINUE_ORPHAN => "KERN_RPC_CONTINUE_ORPHAN", 255 | KERN_RPC_SERVER_TERMINATED => "KERN_RPC_SERVER_TERMINATED", 256 | KERN_RPC_TERMINATE_ORPHAN => "KERN_RPC_TERMINATE_ORPHAN", 257 | KERN_SEMAPHORE_DESTROYED => "KERN_SEMAPHORE_DESTROYED", 258 | KERN_SUCCESS => "KERN_SUCCESS", 259 | KERN_TERMINATED => "KERN_TERMINATED", 260 | KERN_UREFS_OVERFLOW => "KERN_UREFS_OVERFLOW", 261 | v => { 262 | eprintln!("unknown kernel error: {}", v); 263 | "UNKNOWN_KERN_ERROR" 264 | } 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /src/mirrored/mod.rs: -------------------------------------------------------------------------------- 1 | //! Mirrored memory buffer. 2 | mod buffer; 3 | 4 | #[cfg(all( 5 | unix, 6 | not(all( 7 | any( 8 | target_os = "linux", 9 | target_os = "android", 10 | target_os = "macos", 11 | target_os = "ios", 12 | target_os = "openbsd" 13 | ), 14 | not(feature = "unix_sysv") 15 | )) 16 | ))] 17 | mod sysv; 18 | #[cfg(all( 19 | unix, 20 | not(all( 21 | any( 22 | target_os = "linux", 23 | target_os = "android", 24 | target_os = "macos", 25 | target_os = "ios", 26 | target_os = "openbsd" 27 | ), 28 | not(feature = "unix_sysv") 29 | )) 30 | ))] 31 | pub(crate) use self::sysv::{ 32 | allocate_mirrored, allocation_granularity, deallocate_mirrored, 33 | }; 34 | 35 | #[cfg(all( 36 | any(target_os = "linux", target_os = "android", target_os = "openbsd"), 37 | not(feature = "unix_sysv") 38 | ))] 39 | mod linux; 40 | #[cfg(all( 41 | any(target_os = "linux", target_os = "android", target_os = "openbsd"), 42 | not(feature = "unix_sysv") 43 | ))] 44 | pub(crate) use self::linux::{ 45 | allocate_mirrored, allocation_granularity, deallocate_mirrored, 46 | }; 47 | 48 | #[cfg(all( 49 | any(target_os = "macos", target_os = "ios"), 50 | not(feature = "unix_sysv") 51 | ))] 52 | mod macos; 53 | 54 | #[cfg(all( 55 | any(target_os = "macos", target_os = "ios"), 56 | not(feature = "unix_sysv") 57 | ))] 58 | pub(crate) use self::macos::{ 59 | allocate_mirrored, allocation_granularity, deallocate_mirrored, 60 | }; 61 | 62 | #[cfg(target_os = "windows")] 63 | mod winapi; 64 | 65 | #[cfg(target_os = "windows")] 66 | pub(crate) use self::winapi::{ 67 | allocate_mirrored, allocation_granularity, deallocate_mirrored, 68 | }; 69 | 70 | pub use self::buffer::Buffer; 71 | 72 | use super::*; 73 | 74 | /// Allocation error. 75 | pub enum AllocError { 76 | /// The system is Out-of-memory. 77 | Oom, 78 | /// Other allocation errors (not out-of-memory). 79 | /// 80 | /// Race conditions, exhausted file descriptors, etc. 81 | Other, 82 | } 83 | 84 | impl crate::fmt::Debug for AllocError { 85 | fn fmt(&self, f: &mut crate::fmt::Formatter) -> crate::fmt::Result { 86 | match self { 87 | AllocError::Oom => write!(f, "out-of-memory"), 88 | AllocError::Other => write!(f, "other (not out-of-memory)"), 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/mirrored/sysv.rs: -------------------------------------------------------------------------------- 1 | //! Racy System V mirrored memory allocation. 2 | use super::{mem, AllocError}; 3 | use libc::{ 4 | c_int, c_void, mmap, munmap, shmat, shmctl, shmdt, shmget, shmid_ds, 5 | sysconf, IPC_CREAT, IPC_PRIVATE, IPC_RMID, MAP_FAILED, MAP_PRIVATE, 6 | PROT_NONE, _SC_PAGESIZE, 7 | }; 8 | 9 | #[cfg(not(target_os = "macos"))] 10 | use libc::MAP_ANONYMOUS; 11 | 12 | #[cfg(target_os = "macos")] 13 | use libc::MAP_ANON as MAP_ANONYMOUS; 14 | 15 | /// Returns the size of an allocation unit. 16 | /// 17 | /// System V shared memory has the page size as its allocation unit. 18 | pub fn allocation_granularity() -> usize { 19 | unsafe { sysconf(_SC_PAGESIZE) as usize } 20 | } 21 | 22 | /// System V Shared Memory handle. 23 | struct SharedMemory { 24 | id: c_int, 25 | } 26 | 27 | /// Map of System V Shared Memory to an address in the process address space. 28 | struct MemoryMap(*mut c_void); 29 | 30 | impl SharedMemory { 31 | /// Allocates `size` bytes of inter-process shared memory. 32 | /// 33 | /// Return the handle to this shared memory. 34 | /// 35 | /// # Panics 36 | /// 37 | /// If `size` is zero or not a multiple of the allocation granularity. 38 | pub fn allocate(size: usize) -> Result { 39 | assert!(size != 0); 40 | assert!(size % allocation_granularity() == 0); 41 | unsafe { 42 | let id = shmget(IPC_PRIVATE, size, IPC_CREAT | 448); 43 | if id == -1 { 44 | print_error("shmget"); 45 | return Err(AllocError::Oom); 46 | } 47 | Ok(SharedMemory { id }) 48 | } 49 | } 50 | 51 | /// Attaches System V shared memory to the memory address at `ptr` in the 52 | /// address space of the current process. 53 | /// 54 | /// # Panics 55 | /// 56 | /// If `ptr` is null. 57 | pub fn attach(&self, ptr: *mut c_void) -> Result { 58 | unsafe { 59 | // note: the success of allocate guarantees `shm_id != -1`. 60 | assert!(!ptr.is_null()); 61 | let r = shmat(self.id, ptr, 0); 62 | if r as isize == -1 { 63 | print_error("shmat"); 64 | return Err(AllocError::Other); 65 | } 66 | let map = MemoryMap(ptr); 67 | if r != ptr { 68 | print_error("shmat2"); 69 | // map is dropped here, freeing the memory. 70 | return Err(AllocError::Other); 71 | } 72 | Ok(map) 73 | } 74 | } 75 | } 76 | 77 | impl Drop for SharedMemory { 78 | /// Deallocates the inter-process shared memory.. 79 | fn drop(&mut self) { 80 | unsafe { 81 | // note: the success of allocate guarantees `shm_id != -1`. 82 | let r = shmctl(self.id, IPC_RMID, 0 as *mut shmid_ds); 83 | if r == -1 { 84 | // TODO: unlikely 85 | // This should never happen, but just in case: 86 | print_error("shmctl"); 87 | panic!("freeing system V shared-memory failed"); 88 | } 89 | } 90 | } 91 | } 92 | 93 | impl MemoryMap { 94 | /// Initializes a MemoryMap to `ptr`. 95 | /// 96 | /// # Panics 97 | /// 98 | /// If `ptr` is null. 99 | /// 100 | /// # Unsafety 101 | /// 102 | /// If `ptr` does not point to a memory map created using 103 | /// `SharedMemory::attach` that has not been dropped yet.. 104 | unsafe fn from_raw(ptr: *mut c_void) -> MemoryMap { 105 | assert!(!ptr.is_null()); 106 | MemoryMap(ptr) 107 | } 108 | } 109 | 110 | impl Drop for MemoryMap { 111 | fn drop(&mut self) { 112 | unsafe { 113 | // note: the success of SharedMemory::attach and MemoryMap::new 114 | // guarantee `!self.0.is_null()`. 115 | let r = shmdt(self.0); 116 | if r == -1 { 117 | // TODO: unlikely 118 | print_error("shmdt"); 119 | panic!("freeing system V memory map failed"); 120 | } 121 | } 122 | } 123 | } 124 | 125 | /// Allocates `size` bytes of uninitialized memory, where the bytes in range 126 | /// `[0, size / 2)` are mirrored into the bytes in range `[size / 2, size)`. 127 | /// 128 | /// The algorithm using System V interprocess shared-memory is: 129 | /// 130 | /// * 1. Allocate `size / 2` of interprocess shared memory. 131 | /// * 2. Reserve `size` bytes of virtual memory using `mmap + munmap`. 132 | /// * 3. Attach the shared memory to the first and second half. 133 | /// 134 | /// There is a race between steps 2 and 3 because after unmapping the memory 135 | /// and before attaching the shared memory to it another process might use that 136 | /// memory. 137 | pub fn allocate_mirrored(size: usize) -> Result<*mut u8, AllocError> { 138 | unsafe { 139 | assert!(size != 0); 140 | let half_size = size / 2; 141 | assert!(half_size % allocation_granularity() == 0); 142 | 143 | // 1. Allocate interprocess shared memory 144 | let shm = SharedMemory::allocate(half_size)?; 145 | 146 | const MAX_NO_ITERS: i32 = 10; 147 | let mut counter = 0; 148 | let ptr = loop { 149 | counter += 1; 150 | if counter > MAX_NO_ITERS { 151 | return Err(AllocError::Other); 152 | } 153 | 154 | // 2. Reserve virtual memory: 155 | let ptr = mmap( 156 | 0 as *mut c_void, 157 | size, 158 | PROT_NONE, 159 | MAP_ANONYMOUS | MAP_PRIVATE, 160 | -1, 161 | 0, 162 | ); 163 | if ptr == MAP_FAILED { 164 | print_error("mmap initial"); 165 | return Err(AllocError::Oom); 166 | } 167 | 168 | let ptr2 = 169 | (ptr as *mut u8).offset(half_size as isize) as *mut c_void; 170 | 171 | unmap(ptr, size).expect("unmap initial failed"); 172 | 173 | // 3. Attach shared memory to virtual memory: 174 | let map0 = shm.attach(ptr); 175 | if map0.is_err() { 176 | print_error("attach_shm first failed"); 177 | continue; 178 | } 179 | let map1 = shm.attach(ptr2); 180 | if map1.is_err() { 181 | print_error("attach_shm second failed"); 182 | continue; 183 | } 184 | // On success we leak the maps to keep them alive. 185 | // On drop we rebuild the maps from ptr and ptr + half_size 186 | // to deallocate them. 187 | mem::forget(map0); 188 | mem::forget(map1); 189 | break ptr; 190 | }; 191 | 192 | Ok(ptr as *mut u8) 193 | } 194 | } 195 | 196 | /// Deallocates the mirrored memory region at `ptr` of `size` bytes. 197 | /// 198 | /// # Unsafe 199 | /// 200 | /// `ptr` must have been obtained from a call to `allocate_mirrored(size)` and 201 | /// not have been previously deallocated. Otherwise the behavior is undefined. 202 | /// 203 | /// # Panics 204 | /// 205 | /// If `size` is zero or `size / 2` is not a multiple of the 206 | /// allocation granularity, or `ptr` is null. 207 | pub unsafe fn deallocate_mirrored(ptr: *mut u8, size: usize) { 208 | let ptr2 = ptr.offset(size as isize / 2); 209 | MemoryMap::from_raw(ptr as *mut c_void); 210 | MemoryMap::from_raw(ptr2 as *mut c_void); 211 | } 212 | 213 | /// Unmaps the memory region at `[ptr, ptr+size)`. 214 | unsafe fn unmap(ptr: *mut c_void, size: usize) -> Result<(), ()> { 215 | let r = munmap(ptr, size); 216 | if r == -1 { 217 | print_error("unmap"); 218 | return Err(()); 219 | } 220 | Ok(()) 221 | } 222 | 223 | #[cfg(not(all(debug_assertions, feature = "use_std")))] 224 | fn print_error(_location: &str) {} 225 | 226 | #[cfg(all(debug_assertions, feature = "use_std"))] 227 | fn print_error(location: &str) { 228 | eprintln!( 229 | "Error at {}: {}", 230 | location, 231 | ::std::io::Error::last_os_error() 232 | ); 233 | } 234 | -------------------------------------------------------------------------------- /src/mirrored/winapi.rs: -------------------------------------------------------------------------------- 1 | //! Implements the allocator hooks on top of window's virtual alloc. 2 | 3 | use super::mem; 4 | 5 | use winapi::shared::basetsd::SIZE_T; 6 | use winapi::shared::minwindef::{BOOL, DWORD, LPCVOID, LPVOID}; 7 | use winapi::shared::ntdef::LPCWSTR; 8 | use winapi::um::memoryapi::{ 9 | CreateFileMappingW, MapViewOfFileEx, UnmapViewOfFile, VirtualAlloc, 10 | VirtualFree, FILE_MAP_ALL_ACCESS, 11 | }; 12 | use winapi::um::winnt::{ 13 | MEM_RELEASE, MEM_RESERVE, PAGE_NOACCESS, PAGE_READWRITE, SEC_COMMIT, 14 | }; 15 | 16 | use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE}; 17 | use winapi::um::minwinbase::LPSECURITY_ATTRIBUTES; 18 | use winapi::um::sysinfoapi::{GetSystemInfo, LPSYSTEM_INFO, SYSTEM_INFO}; 19 | 20 | pub use winapi::shared::ntdef::HANDLE; 21 | 22 | use super::AllocError; 23 | 24 | /// Returns the size of an allocation unit in bytes. 25 | /// 26 | /// In Windows calls to `VirtualAlloc` must specify a multiple of 27 | /// `SYSTEM_INFO::dwAllocationGranularity` bytes. 28 | /// 29 | /// FIXME: the allocation granularity should always be larger than the page 30 | /// size (64k vs 4k), so determining the page size here is not necessary. 31 | pub fn allocation_granularity() -> usize { 32 | unsafe { 33 | let mut system_info = mem::MaybeUninit::::uninit(); 34 | GetSystemInfo(system_info.as_mut_ptr() as LPSYSTEM_INFO); 35 | let system_info = system_info.assume_init(); 36 | let allocation_granularity = 37 | system_info.dwAllocationGranularity as usize; 38 | let page_size = system_info.dwPageSize as usize; 39 | page_size.max(allocation_granularity) 40 | } 41 | } 42 | 43 | /// Allocates an uninitialzied buffer that holds `size` bytes, where 44 | /// the bytes in range `[0, size / 2)` are mirrored into the bytes in 45 | /// range `[size / 2, size)`. 46 | /// 47 | /// On Windows the algorithm is as follows: 48 | /// 49 | /// * 1. Allocate physical memory to hold `size / 2` bytes using a memory 50 | /// mapped file. 51 | /// * 2. Find a region of virtual memory large enough to hold `size` 52 | /// bytes (by allocating memory with `VirtualAlloc` and immediately 53 | /// freeing it with `VirtualFree`). 54 | /// * 3. Race condition: map the physical memory to the two halves of the 55 | /// virtual memory region. 56 | /// 57 | /// If we get a race (e.g. because some other process obtains memory in the 58 | /// memory region where we wanted to map our physical memory) we release 59 | /// the first portion of virtual memory if mapping succeeded and try 60 | /// again (up to a maximum of `MAX_NO_ALLOC_ITERS` times). 61 | /// 62 | /// # Panics 63 | /// 64 | /// If `size` is zero or `size / 2` is not a multiple of the 65 | /// allocation granularity. 66 | pub fn allocate_mirrored(size: usize) -> Result<*mut u8, AllocError> { 67 | /// Maximum number of attempts to allocate in case of a race condition. 68 | const MAX_NO_ALLOC_ITERS: usize = 10; 69 | unsafe { 70 | let half_size = size / 2; 71 | assert!(size != 0); 72 | assert!(half_size % allocation_granularity() == 0); 73 | 74 | let file_mapping = create_file_mapping(half_size)?; 75 | 76 | let mut no_iters = 0; 77 | let virt_ptr = loop { 78 | if no_iters > MAX_NO_ALLOC_ITERS { 79 | // If we exceeded the number of iterations try to close the 80 | // handle and error: 81 | close_file_mapping(file_mapping) 82 | .expect("freeing physical memory failed"); 83 | return Err(AllocError::Other); 84 | } 85 | 86 | // Find large enough virtual memory region (if this fails we are 87 | // done): 88 | let virt_ptr = reserve_virtual_memory(size)?; 89 | 90 | // Map the physical memory to the first half: 91 | if map_view_of_file(file_mapping, half_size, virt_ptr).is_err() { 92 | // If this fails, there is nothing to free and we try again: 93 | no_iters += 1; 94 | continue; 95 | } 96 | 97 | // Map physical memory to the second half: 98 | if map_view_of_file( 99 | file_mapping, 100 | half_size, 101 | virt_ptr.offset(half_size as isize), 102 | ) 103 | .is_err() 104 | { 105 | // If this fails, we release the map of the first half and try 106 | // again: 107 | no_iters += 1; 108 | if unmap_view_of_file(virt_ptr).is_err() { 109 | // If unmapping fails try to close the handle and 110 | // panic: 111 | close_file_mapping(file_mapping) 112 | .expect("freeing physical memory failed"); 113 | panic!("unmapping first half of memory failed") 114 | } 115 | continue; 116 | } 117 | 118 | // We are done 119 | break virt_ptr; 120 | }; 121 | 122 | // Close the file handle, it will be released when all the memory is 123 | // unmapped: 124 | close_file_mapping(file_mapping).expect("closing file handle failed"); 125 | 126 | Ok(virt_ptr) 127 | } 128 | } 129 | 130 | /// Deallocates the mirrored memory region at `ptr` of `size` bytes. 131 | /// 132 | /// # Unsafe 133 | /// 134 | /// `ptr` must have been obtained from a call to `allocate_mirrored(size)`, 135 | /// otherwise the behavior is undefined. 136 | /// 137 | /// # Panics 138 | /// 139 | /// If `size` is zero or `size / 2` is not a multiple of the 140 | /// allocation granularity, or `ptr` is null. 141 | pub unsafe fn deallocate_mirrored(ptr: *mut u8, size: usize) { 142 | assert!(!ptr.is_null()); 143 | assert!(size != 0); 144 | assert!(size % allocation_granularity() == 0); 145 | // On "windows" we unmap the memory. 146 | let half_size = size / 2; 147 | unmap_view_of_file(ptr).expect("unmapping first buffer half failed"); 148 | let second_half_ptr = ptr.offset(half_size as isize); 149 | unmap_view_of_file(second_half_ptr) 150 | .expect("unmapping second buffer half failed"); 151 | } 152 | 153 | /// Creates a file mapping able to hold `size` bytes. 154 | /// 155 | /// # Panics 156 | /// 157 | /// If `size` is zero or not a multiple of the `allocation_granularity`. 158 | fn create_file_mapping(size: usize) -> Result { 159 | unsafe { 160 | assert!(size != 0); 161 | assert!(size % allocation_granularity() == 0); 162 | let dw_maximum_size_low: DWORD = size as DWORD; 163 | let dw_maximum_size_high: DWORD = 164 | match (mem::size_of::(), mem::size_of::()) { 165 | // If both sizes are equal, the size is passed in the lower 166 | // half, so the higher 32-bits are zero 167 | (4, 4) | (8, 8) => 0, 168 | // If DWORD is 32 bit but usize is 64-bit, we pass the higher 169 | // 32-bit of size: 170 | (4, 8) => (size >> 32) as DWORD, 171 | _ => unimplemented!(), 172 | }; 173 | 174 | let h: HANDLE = CreateFileMappingW( 175 | /* hFile: */ INVALID_HANDLE_VALUE as HANDLE, 176 | /* lpAttributes: */ 0 as LPSECURITY_ATTRIBUTES, 177 | /* flProtect: */ PAGE_READWRITE | SEC_COMMIT as DWORD, 178 | /* dwMaximumSizeHigh: */ dw_maximum_size_high, 179 | /* dwMaximumSizeLow: */ dw_maximum_size_low, 180 | /* lpName: */ 0 as LPCWSTR, 181 | ); 182 | 183 | if h.is_null() { 184 | let s = tiny_str!("create_file_mapping (with size: {})", size); 185 | print_error(s.as_str()); 186 | return Err(AllocError::Oom); 187 | } 188 | Ok(h) 189 | } 190 | } 191 | 192 | /// Closes a file mapping. 193 | /// 194 | /// # Unsafety 195 | /// 196 | /// `file_mapping` must point to a valid file mapping created with 197 | /// `create_file_mapping`. 198 | /// 199 | /// # Panics 200 | /// 201 | /// If `file_mapping` is null. 202 | unsafe fn close_file_mapping(file_mapping: HANDLE) -> Result<(), ()> { 203 | assert!(!file_mapping.is_null()); 204 | 205 | let r: BOOL = CloseHandle(file_mapping); 206 | if r == 0 { 207 | print_error("close_file_mapping"); 208 | return Err(()); 209 | } 210 | Ok(()) 211 | } 212 | 213 | /// Reserves a virtual memory region able to hold `size` bytes. 214 | /// 215 | /// The Windows API has no way to do this, so... we allocate a `size`-ed region 216 | /// with `VirtualAlloc`, immediately free it afterwards with `VirtualFree` and 217 | /// hope that the region is still available when we try to map into it. 218 | /// 219 | /// # Panics 220 | /// 221 | /// If `size` is not a multiple of the `allocation_granularity`. 222 | fn reserve_virtual_memory(size: usize) -> Result<(*mut u8), AllocError> { 223 | unsafe { 224 | assert!(size != 0); 225 | assert!(size % allocation_granularity() == 0); 226 | 227 | let r: LPVOID = VirtualAlloc( 228 | /* lpAddress: */ 0 as LPVOID, 229 | /* dwSize: */ size as SIZE_T, 230 | /* flAllocationType: */ MEM_RESERVE, 231 | /* flProtect: */ PAGE_NOACCESS, 232 | ); 233 | 234 | if r.is_null() { 235 | print_error("reserve_virtual_memory(alloc failed)"); 236 | return Err(AllocError::Oom); 237 | } 238 | 239 | let fr = VirtualFree( 240 | /* lpAddress: */ r, 241 | /* dwSize: */ 0 as SIZE_T, 242 | /* dwFreeType: */ MEM_RELEASE as DWORD, 243 | ); 244 | if fr == 0 { 245 | print_error("reserve_virtual_memory(free failed)"); 246 | return Err(AllocError::Other); 247 | } 248 | 249 | Ok(r as *mut u8) 250 | } 251 | } 252 | 253 | /// Maps `size` bytes of `file_mapping` to `address`. 254 | /// 255 | /// # Unsafety 256 | /// 257 | /// `file_mapping` must point to a valid file-mapping created with 258 | /// `create_file_mapping`. 259 | /// 260 | /// # Panics 261 | /// 262 | /// If `file_mapping` or `address` are null, or if `size` is zero or not a 263 | /// multiple of the allocation granularity of the system. 264 | unsafe fn map_view_of_file( 265 | file_mapping: HANDLE, size: usize, address: *mut u8, 266 | ) -> Result<(), ()> { 267 | assert!(!file_mapping.is_null()); 268 | assert!(!address.is_null()); 269 | assert!(size != 0); 270 | assert!(size % allocation_granularity() == 0); 271 | 272 | let r: LPVOID = MapViewOfFileEx( 273 | /* hFileMappingObject: */ file_mapping, 274 | /* dwDesiredAccess: */ FILE_MAP_ALL_ACCESS, 275 | /* dwFileOffsetHigh: */ 0 as DWORD, 276 | /* dwFileOffsetLow: */ 0 as DWORD, 277 | /* dwNumberOfBytesToMap: */ size as SIZE_T, 278 | /* lpBaseAddress: */ address as LPVOID, 279 | ); 280 | if r.is_null() { 281 | print_error("map_view_of_file"); 282 | return Err(()); 283 | } 284 | debug_assert!(r == address as LPVOID); 285 | Ok(()) 286 | } 287 | 288 | /// Unmaps the memory at `address`. 289 | /// 290 | /// # Unsafety 291 | /// 292 | /// If address does not point to a valid memory address previously mapped with 293 | /// `map_view_of_file`. 294 | /// 295 | /// # Panics 296 | /// 297 | /// If `address` is null. 298 | unsafe fn unmap_view_of_file(address: *mut u8) -> Result<(), ()> { 299 | assert!(!address.is_null()); 300 | 301 | let r = UnmapViewOfFile(/* lpBaseAddress: */ address as LPCVOID); 302 | if r == 0 { 303 | print_error("unmap_view_of_file"); 304 | return Err(()); 305 | } 306 | Ok(()) 307 | } 308 | 309 | /// Prints last os error at `location`. 310 | #[cfg(all(debug_assertions, feature = "use_std"))] 311 | fn print_error(location: &str) { 312 | eprintln!( 313 | "Error at {}: {}", 314 | location, 315 | ::std::io::Error::last_os_error() 316 | ); 317 | } 318 | 319 | /// Prints last os error at `location`. 320 | #[cfg(not(all(debug_assertions, feature = "use_std")))] 321 | fn print_error(_location: &str) {} 322 | --------------------------------------------------------------------------------