├── .github └── workflows │ └── rust.yml ├── .gitignore ├── .vscode └── cspell.json ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── UPGRADING.md ├── benches └── benches.rs ├── crates ├── fuzzing-support │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── build.rs │ └── src │ │ ├── allocator_api.rs │ │ ├── bump_down.rs │ │ ├── bump_prepare_down.rs │ │ ├── bump_prepare_up.rs │ │ ├── bump_up.rs │ │ ├── bumping.rs │ │ ├── chunk_size.rs │ │ ├── from_bump_scope.rs │ │ ├── from_bump_scope │ │ ├── bumping.rs │ │ └── chunk_size_config.rs │ │ ├── lib.rs │ │ └── many_vecs.rs ├── inspect-asm │ └── README.md ├── test-fallibility │ ├── .cargo │ │ └── config.toml │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── justfile │ └── src │ │ └── lib.rs ├── test-hashbrown │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── tests-from-std │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── rustfmt.toml │ └── src │ ├── bump_string.rs │ ├── bump_vec.rs │ ├── lib.rs │ ├── mut_bump_vec.rs │ └── mut_bump_vec_rev.rs ├── fuzz ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── allocator-cov.nu ├── fuzz_targets │ ├── allocator.rs │ ├── bump_down.rs │ ├── bump_greedy_down.rs │ ├── bump_greedy_up.rs │ ├── bump_up.rs │ ├── bumping.rs │ ├── chunk_size.rs │ ├── many_vecs.rs │ ├── slice_split_off.rs │ └── vec_split_off.rs └── justfile ├── justfile ├── rustfmt.toml ├── src ├── alloc.rs ├── alloc │ ├── global.rs │ └── system.rs ├── allocator_impl.rs ├── bump.rs ├── bump_align_guard.rs ├── bump_allocator.rs ├── bump_allocator_scope.rs ├── bump_box.rs ├── bump_box │ └── slice_initializer.rs ├── bump_pool.rs ├── bump_scope.rs ├── bump_scope_guard.rs ├── bump_string.rs ├── bump_vec.rs ├── bump_vec │ ├── drain.rs │ ├── into_iter.rs │ └── splice.rs ├── bumping.rs ├── chunk_header.rs ├── chunk_size.rs ├── chunk_size │ └── chunk_size_config.rs ├── destructure.rs ├── error_behavior.rs ├── features │ ├── allocator_api2_02.rs │ ├── allocator_api2_03.rs │ ├── bytemuck.rs │ ├── bytemuck │ │ └── vec_ext.rs │ ├── mod.rs │ ├── nightly_allocator_api.rs │ ├── serde.rs │ ├── zerocopy_08.rs │ └── zerocopy_08 │ │ └── vec_ext.rs ├── fixed_bump_string.rs ├── fixed_bump_vec.rs ├── from_utf16_error.rs ├── from_utf8_error.rs ├── layout.rs ├── lib.rs ├── mut_bump_allocator.rs ├── mut_bump_allocator_scope.rs ├── mut_bump_string.rs ├── mut_bump_vec.rs ├── mut_bump_vec │ └── into_iter.rs ├── mut_bump_vec_rev.rs ├── no_drop.rs ├── owned_slice.rs ├── owned_slice │ ├── drain.rs │ ├── extract_if.rs │ └── into_iter.rs ├── owned_str.rs ├── owned_str │ └── drain.rs ├── partial_eq.rs ├── polyfill │ ├── hint.rs │ ├── iter.rs │ ├── layout.rs │ ├── mod.rs │ ├── non_null.rs │ ├── option.rs │ ├── pointer.rs │ ├── pointer_mut.rs │ ├── ptr.rs │ ├── slice.rs │ ├── str.rs │ ├── str │ │ ├── lossy.rs │ │ └── validations.rs │ └── usize.rs ├── raw_bump_box.rs ├── raw_chunk.rs ├── raw_fixed_bump_string.rs ├── raw_fixed_bump_vec.rs ├── set_len_on_drop.rs ├── set_len_on_drop_by_ptr.rs ├── stats.rs ├── stats │ └── any.rs ├── tests │ ├── alloc_cstr.rs │ ├── alloc_fmt.rs │ ├── alloc_iter.rs │ ├── alloc_slice.rs │ ├── alloc_try_with.rs │ ├── allocator_api.rs │ ├── append.rs │ ├── bump_allocator.rs │ ├── bump_string.rs │ ├── bump_vec.rs │ ├── bump_vec_doc.rs │ ├── chunk_size.rs │ ├── coerce_unsized.rs │ ├── fixed_bump_vec.rs │ ├── fn_traits.rs │ ├── grow_vec.rs │ ├── into_flattened.rs │ ├── io_write.rs │ ├── limited_allocator.rs │ ├── mod.rs │ ├── mut_bump_vec_doc.rs │ ├── mut_bump_vec_rev_doc.rs │ ├── mut_collections_do_not_waste_space.rs │ ├── panic_safety.rs │ ├── pool.rs │ ├── serde.rs │ ├── split_off.rs │ ├── test_wrap.rs │ ├── unaligned_collection.rs │ ├── unallocated.rs │ └── vec.rs └── without_dealloc.rs └── tests ├── compile_fail.rs ├── compile_fail ├── escape_closure.rs ├── escape_closure.stderr ├── escape_closure_scope.rs ├── escape_closure_scope.stderr ├── escape_guard.rs ├── escape_guard.stderr ├── escape_guard_scope.rs ├── escape_guard_scope.stderr ├── multiple_scopes.rs ├── multiple_scopes.stderr ├── unsize_to_different_lifetime.rs └── unsize_to_different_lifetime.stderr ├── limit_memory_usage.rs ├── static_memory.rs └── thread_local.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | branches: ["main"] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | RUST_BACKTRACE: 1 12 | 13 | jobs: 14 | format: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - run: cargo fmt --all -- --check 19 | - run: cd crates/fuzzing-support && cargo fmt --all -- --check 20 | - run: cd crates/test-fallibility && cargo fmt --all -- --check 21 | - run: cd crates/tests-from-std && cargo fmt --all -- --check 22 | - run: cd fuzz && cargo fmt --all -- --check 23 | check-msrv: 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v4 27 | - uses: dtolnay/rust-toolchain@1.64.0 28 | - run: cargo check --no-default-features 29 | - run: cargo check --features serde,zerocopy-08,allocator-api2-02 30 | check-stable: 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v4 34 | - uses: dtolnay/rust-toolchain@stable 35 | with: 36 | components: clippy 37 | - run: cargo clippy --tests --no-default-features 38 | - run: cargo clippy --tests --features serde,zerocopy-08,allocator-api2-02,allocator-api2-03 39 | check-nightly: 40 | runs-on: ubuntu-latest 41 | steps: 42 | - uses: actions/checkout@v4 43 | - uses: dtolnay/rust-toolchain@nightly 44 | with: 45 | components: clippy 46 | - run: cargo clippy --tests --no-default-features 47 | - run: cargo clippy --tests --features serde,zerocopy-08,allocator-api2-02,allocator-api2-03 48 | - run: cargo clippy --tests --benches --all-features 49 | - run: cd crates/test-hashbrown && cargo clippy 50 | - run: cd crates/test-hashbrown && cargo clippy --all-features 51 | - run: cd crates/tests-from-std && cargo clippy 52 | - run: cd crates/test-fallibility && cargo clippy 53 | test-stable: 54 | runs-on: ubuntu-latest 55 | steps: 56 | - uses: actions/checkout@v4 57 | - uses: dtolnay/rust-toolchain@stable 58 | - run: cargo test --features serde,zerocopy-08,allocator-api2-02,allocator-api2-03 59 | test-nightly: 60 | runs-on: ubuntu-latest 61 | env: 62 | MIRIFLAGS: "-Zmiri-strict-provenance" 63 | steps: 64 | - uses: actions/checkout@v4 65 | - uses: dtolnay/rust-toolchain@nightly 66 | with: 67 | components: miri 68 | - run: cargo test --all-features 69 | - run: cargo miri test --all-features 70 | - run: cd crates/test-hashbrown && cargo test 71 | - run: cd crates/test-hashbrown && cargo test --all-features 72 | - run: cd crates/test-hashbrown && cargo miri test 73 | - run: cd crates/test-hashbrown && cargo miri test --all-features 74 | - run: cd crates/tests-from-std && cargo test 75 | - run: cd crates/tests-from-std && cargo miri test 76 | - run: cd crates/fuzzing-support && cargo test 77 | - run: cd crates/fuzzing-support && cargo miri test 78 | minimal-versions: 79 | runs-on: ubuntu-latest 80 | steps: 81 | - uses: actions/checkout@v4 82 | - uses: dtolnay/rust-toolchain@nightly 83 | - uses: taiki-e/install-action@cargo-hack 84 | - uses: taiki-e/install-action@cargo-minimal-versions 85 | - uses: Swatinem/rust-cache@v2 86 | - run: cargo minimal-versions check 87 | - run: cargo minimal-versions check --all-features 88 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.vscode/** 3 | !/.vscode/cspell.json 4 | /rustc-ice-* -------------------------------------------------------------------------------- /.vscode/cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2", 3 | "language": "en", 4 | "words": [ 5 | "abcdecde", 6 | "abcdecdeab", 7 | "abcdecdeabecde", 8 | "alloc", 9 | "alloctests", 10 | "Appendable", 11 | "arrayref", 12 | "autorefs", 13 | "baaz", 14 | "backshift", 15 | "backshifted", 16 | "barbaz", 17 | "barbazqux", 18 | "btreemap", 19 | "bufs", 20 | "bumpalo", 21 | "bumpbox", 22 | "bytemuck", 23 | "byteslice", 24 | "cfgs", 25 | "clippy", 26 | "codegen", 27 | "codepoint", 28 | "Comparand", 29 | "compat", 30 | "constness", 31 | "cstr", 32 | "curr", 33 | "Dealloc", 34 | "deallocate", 35 | "deallocates", 36 | "deallocating", 37 | "deallocation", 38 | "dedup", 39 | "deinitialize", 40 | "Deque", 41 | "derefs", 42 | "dlmalloc", 43 | "docsrs", 44 | "doesnt", 45 | "dont", 46 | "dtolnay", 47 | "dvec", 48 | "ệfoobar", 49 | "ệfooยbar", 50 | "elems", 51 | "elidable", 52 | "emscripten", 53 | "expl", 54 | "factly", 55 | "FFFD", 56 | "foobarbaz", 57 | "foobarbazqux", 58 | "footgun", 59 | "genericity", 60 | "haha", 61 | "hashbrown", 62 | "Hasher", 63 | "impls", 64 | "inplace", 65 | "insertable", 66 | "jemalloc", 67 | "justfile", 68 | "kenobi", 69 | "libc", 70 | "libfuzzer", 71 | "maybeuninits", 72 | "Mebibyte", 73 | "memmove", 74 | "miri", 75 | "MIRIFLAGS", 76 | "msrv", 77 | "mustnt", 78 | "nonnull", 79 | "nonoverlapping", 80 | "nonoverlappingly", 81 | "nostd", 82 | "nushell", 83 | "orld", 84 | "overaligned", 85 | "Peekable", 86 | "pointee", 87 | "pointees", 88 | "println", 89 | "proto", 90 | "rande", 91 | "rangemap", 92 | "rdme", 93 | "realloc", 94 | "reborrowing", 95 | "repr", 96 | "rfind", 97 | "roundtripped", 98 | "rustc", 99 | "rustdoc", 100 | "rustfmt", 101 | "rustup", 102 | "rustversion", 103 | "serde", 104 | "shrinknt", 105 | "sinkptr", 106 | "slicemut", 107 | "splittable", 108 | "sptr", 109 | "srcptr", 110 | "structs", 111 | "Swatinem", 112 | "sysroot", 113 | "taiki", 114 | "tlsv", 115 | "trybuild", 116 | "Uninit", 117 | "uninlined", 118 | "unitialized", 119 | "unsize", 120 | "Unsized", 121 | "unsizing", 122 | "unyielded", 123 | "usize", 124 | "valgrind", 125 | "Vec's", 126 | "Vecs", 127 | "vtable", 128 | "Zeroable", 129 | "zerocopy", 130 | "Zmiri" 131 | ] 132 | } 133 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bump-scope" 3 | version = "0.17.0" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | description = "A fast bump allocator that supports allocation scopes / checkpoints. Aka an arena for values of arbitrary types." 7 | categories = ["memory-management", "no-std", "no-std::no-alloc"] 8 | keywords = ["allocator", "arena", "no-std", "bump", "allocation"] 9 | documentation = "https://docs.rs/bump-scope" 10 | repository = "https://github.com/bluurryy/bump-scope" 11 | rust-version = "1.64.0" 12 | readme = "README.md" 13 | exclude = ["*.nu", "justfile", "tests", ".vscode", ".github"] 14 | 15 | [dependencies] 16 | allocator-api2-02 = { package = "allocator-api2", version = "0.2.21", default-features = false, optional = true } 17 | allocator-api2-03 = { package = "allocator-api2", version = "0.3.0", default-features = false, optional = true } 18 | bytemuck = { version = "1.23.0", default-features = false, optional = true } 19 | zerocopy-08 = { package = "zerocopy", version = "0.8.14", default-features = false, optional = true } 20 | serde = { version = "1.0.217", optional = true } 21 | rustversion = "1.0.19" 22 | 23 | [features] 24 | default = ["std", "alloc", "panic-on-alloc"] 25 | 26 | ## Adds `BumpPool` and implementations of `std::io` traits. 27 | std = ["alloc", "allocator-api2-02?/std", "allocator-api2-03?/std"] 28 | 29 | ## Adds `Global` as the default base allocator and some interactions with `alloc` collections. 30 | alloc = ["allocator-api2-02?/alloc", "allocator-api2-03?/alloc"] 31 | 32 | ## Adds functions and traits that will panic when the allocation fails. 33 | ## Without this feature, allocation failures cannot cause panics, and only 34 | ## `try_`-prefixed allocation methods will be available. 35 | panic-on-alloc = [] 36 | 37 | ## Adds `Serialize` implementations for `BumpBox`, strings and vectors, and `DeserializeSeed` for strings and vectors. 38 | serde = ["dep:serde"] 39 | 40 | ## Adds `bytemuck::*` extension traits for `alloc_zeroed(_slice)`, `BumpBox::init_zeroed` and 41 | ## `resize_zeroed` and `extend_zeroed` for vector types. 42 | bytemuck = ["dep:bytemuck"] 43 | 44 | ## Adds `zerocopy_08::*` extension traits for `alloc_zeroed(_slice)`, `BumpBox::init_zeroed` and 45 | ## `resize_zeroed` and `extend_zeroed` for vector types. 46 | zerocopy-08 = ["dep:zerocopy-08"] 47 | 48 | ## Makes `Bump(Scope)` implement `allocator_api2` version `0.2`'s `Allocator` and 49 | ## makes it possible to use an `allocator_api2::alloc::Allocator` as a base allocator via 50 | ## [`AllocatorApiV02Compat`](crate::alloc::compat::AllocatorApi2V02Compat). 51 | allocator-api2-02 = ["dep:allocator-api2-02"] 52 | 53 | ## Makes `Bump(Scope)` implement `allocator_api2` version `0.3`'s `Allocator` and 54 | ## makes it possible to use an `allocator_api2::alloc::Allocator` as a base allocator via 55 | ## [`AllocatorApiV03Compat`](crate::alloc::compat::AllocatorApi2V03Compat). 56 | allocator-api2-03 = ["dep:allocator-api2-03"] 57 | 58 | #! ### Nightly features 59 | 60 | ## Enables all other nightly feature flags. 61 | nightly = [ 62 | "nightly-allocator-api", 63 | "nightly-coerce-unsized", 64 | "nightly-exact-size-is-empty", 65 | "nightly-trusted-len", 66 | "nightly-fn-traits", 67 | "nightly-tests", 68 | ] 69 | 70 | ## Makes `Bump(Scope)` implement `alloc`'s `Allocator` and 71 | ## allows using an `alloc::alloc::Allocator` as a base allocator via 72 | ## [`AllocatorNightlyCompat`](crate::alloc::compat::AllocatorNightlyCompat). 73 | ## 74 | ## This will also enable `allocator-api2` version `0.2`'s `nightly` feature. 75 | nightly-allocator-api = ["allocator-api2-02/nightly"] 76 | 77 | ## Makes `BumpBox` implement [`CoerceUnsized`](core::ops::CoerceUnsized). 78 | ## With this `BumpBox<[i32;3]>` coerces to `BumpBox<[i32]>`, `BumpBox` and so on. 79 | ## You can unsize a `BumpBox` in stable without this feature using [`unsize_bump_box`]. 80 | nightly-coerce-unsized = [] 81 | 82 | ## Implements `is_empty` manually for some iterators. 83 | nightly-exact-size-is-empty = [] 84 | 85 | ## Implements `TrustedLen` for some iterators. 86 | nightly-trusted-len = [] 87 | 88 | ## Implements `Fn*` traits for `BumpBox`. Makes `BumpBox` callable. Requires alloc crate. 89 | nightly-fn-traits = [] 90 | 91 | ## Enables some tests that require a nightly compiler. 92 | nightly-tests = [] 93 | 94 | [dev-dependencies] 95 | trybuild = "1.0.90" 96 | expect-test = "1.4.1" 97 | criterion = { version = "0.5.1", features = ["html_reports"] } 98 | bumpalo = "3.16.0" 99 | rayon = "1.10.0" 100 | serde_json = "1.0.115" 101 | document-features = "0.2.8" 102 | 103 | [package.metadata.docs.rs] 104 | all-features = true 105 | rustdoc-args = ["--generate-link-to-definition"] 106 | 107 | [package.metadata.release] 108 | allow-branch = ["main"] 109 | pre-release-hook = ["just", "pre-release"] 110 | pre-release-commit-message = "release: version {{version}}" 111 | pre-release-replacements = [ 112 | { file = "CHANGELOG.md", search = "## \\[Unreleased\\]", replace = "## [{{version}}] - {{date}}" }, 113 | { file = "CHANGELOG.md", search = "\\[Unreleased\\]: .*", replace = "[{{version}}]: https://github.com/bluurryy/bump-scope/releases/tag/v{{version}}" }, 114 | { file = "CHANGELOG.md", search = "#Unreleased", replace = "#{{version}}" }, 115 | { file = "UPGRADING.md", search = "## \\[Unreleased\\]", replace = "## [{{version}}] - {{date}}" }, 116 | { file = "UPGRADING.md", search = "\\[Unreleased\\]: .*", replace = "[{{version}}]: CHANGELOG.md#{{version}}" }, 117 | { file = "UPGRADING.md", search = "#Unreleased", replace = "#{{version}}" }, 118 | ] 119 | 120 | [[bench]] 121 | name = "benches" 122 | harness = false 123 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /UPGRADING.md: -------------------------------------------------------------------------------- 1 | ## [0.17.0] - 2025-05-25 2 | 3 | - If you are bump allocating `hashbrown` types you have to enable the `allocator-api2-02` feature unless you are using `hashbrown`'s `nightly` feature then you only need the `nightly-allocator-api` feature. 4 | - If you are using the methods `alloc_zeroed(_slice)`, `init_zeroed`, `resize_zeroed` or `extend_zeroed` you now have to enable the `zerocopy-08` feature (instead of `zerocopy`) and import the respective extension traits from `bump_scope::zerocopy_08`. 5 | - If you were using a custom base allocator you now have to make it implement this crate's `Allocator` trait. You can safely do that using a wrapper type from the `bump_scope::alloc::compat` module. 6 | - Uses of `bump_format!(in bump, ...` where `bump` is not a reference now have to write `bump_format!(in &bump, ...` for the same behavior 7 | - There are other less impactful breaking changes that you might encounter. [You can read about them in the changelog.](CHANGELOG.md#0.17.0) 8 | 9 | [0.17.0]: CHANGELOG.md#0.17.0 -------------------------------------------------------------------------------- /crates/fuzzing-support/.gitignore: -------------------------------------------------------------------------------- 1 | /target -------------------------------------------------------------------------------- /crates/fuzzing-support/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "allocator-api2" 7 | version = "0.2.21" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 10 | 11 | [[package]] 12 | name = "allocator-api2" 13 | version = "0.3.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "78200ac3468a57d333cd0ea5dd398e25111194dcacd49208afca95c629a6311d" 16 | 17 | [[package]] 18 | name = "arbitrary" 19 | version = "1.3.2" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" 22 | dependencies = [ 23 | "derive_arbitrary", 24 | ] 25 | 26 | [[package]] 27 | name = "bump-scope" 28 | version = "0.17.0" 29 | dependencies = [ 30 | "allocator-api2 0.2.21", 31 | "allocator-api2 0.3.0", 32 | "rustversion", 33 | ] 34 | 35 | [[package]] 36 | name = "derive_arbitrary" 37 | version = "1.3.2" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" 40 | dependencies = [ 41 | "proc-macro2", 42 | "quote", 43 | "syn", 44 | ] 45 | 46 | [[package]] 47 | name = "fuzzing-support" 48 | version = "0.0.0" 49 | dependencies = [ 50 | "arbitrary", 51 | "bump-scope", 52 | "rangemap", 53 | "zerocopy", 54 | ] 55 | 56 | [[package]] 57 | name = "proc-macro2" 58 | version = "1.0.79" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" 61 | dependencies = [ 62 | "unicode-ident", 63 | ] 64 | 65 | [[package]] 66 | name = "quote" 67 | version = "1.0.35" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 70 | dependencies = [ 71 | "proc-macro2", 72 | ] 73 | 74 | [[package]] 75 | name = "rangemap" 76 | version = "1.5.1" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" 79 | 80 | [[package]] 81 | name = "rustversion" 82 | version = "1.0.19" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" 85 | 86 | [[package]] 87 | name = "syn" 88 | version = "2.0.55" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" 91 | dependencies = [ 92 | "proc-macro2", 93 | "quote", 94 | "unicode-ident", 95 | ] 96 | 97 | [[package]] 98 | name = "unicode-ident" 99 | version = "1.0.12" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 102 | 103 | [[package]] 104 | name = "zerocopy" 105 | version = "0.8.7" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "bb3da5f7220f919a6c7af7c856435a68ee1582fd7a77aa72936257d8335bd6f6" 108 | dependencies = [ 109 | "zerocopy-derive", 110 | ] 111 | 112 | [[package]] 113 | name = "zerocopy-derive" 114 | version = "0.8.7" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "2e5f54f3cc93cd80745404626681b4b9fca9a867bad5a8424b618eb0db1ae6ea" 117 | dependencies = [ 118 | "proc-macro2", 119 | "quote", 120 | "syn", 121 | ] 122 | -------------------------------------------------------------------------------- /crates/fuzzing-support/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fuzzing-support" 3 | version = "0.0.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | bump-scope = { path = "../.." } 8 | arbitrary = { version = "1.3.2", features = ["derive"] } 9 | rangemap = "1.5.1" 10 | zerocopy = { version = "0.8.7", features = ["derive"] } 11 | -------------------------------------------------------------------------------- /crates/fuzzing-support/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("cargo:rustc-check-cfg=cfg(fuzzing_repro)"); 3 | } 4 | -------------------------------------------------------------------------------- /crates/fuzzing-support/src/bump_down.rs: -------------------------------------------------------------------------------- 1 | use arbitrary::Arbitrary; 2 | 3 | use crate::{from_bump_scope, FuzzBumpProps}; 4 | 5 | #[derive(Debug, Arbitrary)] 6 | pub struct Fuzz { 7 | props: FuzzBumpProps, 8 | } 9 | 10 | impl Fuzz { 11 | pub fn run(self) { 12 | let props = self.props.for_down().to(); 13 | from_bump_scope::bumping::bump_down(props); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /crates/fuzzing-support/src/bump_prepare_down.rs: -------------------------------------------------------------------------------- 1 | use arbitrary::Arbitrary; 2 | 3 | use crate::{from_bump_scope, FuzzBumpPrepareProps}; 4 | 5 | #[derive(Debug, Arbitrary)] 6 | pub struct Fuzz { 7 | props: FuzzBumpPrepareProps, 8 | } 9 | 10 | impl Fuzz { 11 | pub fn run(self) { 12 | let props = self.props.for_down().to(); 13 | from_bump_scope::bumping::bump_prepare_down(props); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /crates/fuzzing-support/src/bump_prepare_up.rs: -------------------------------------------------------------------------------- 1 | use arbitrary::Arbitrary; 2 | 3 | use crate::{from_bump_scope, FuzzBumpPrepareProps}; 4 | 5 | #[derive(Debug, Arbitrary)] 6 | pub struct Fuzz { 7 | props: FuzzBumpPrepareProps, 8 | } 9 | 10 | impl Fuzz { 11 | pub fn run(self) { 12 | let props = self.props.for_up().to(); 13 | from_bump_scope::bumping::bump_prepare_up(props); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /crates/fuzzing-support/src/bump_up.rs: -------------------------------------------------------------------------------- 1 | use arbitrary::Arbitrary; 2 | 3 | use crate::{from_bump_scope, FuzzBumpProps}; 4 | 5 | #[derive(Debug, Arbitrary)] 6 | pub struct Fuzz { 7 | props: FuzzBumpProps, 8 | } 9 | 10 | impl Fuzz { 11 | pub fn run(self) { 12 | let props = self.props.for_up().to(); 13 | from_bump_scope::bumping::bump_up(props); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /crates/fuzzing-support/src/from_bump_scope.rs: -------------------------------------------------------------------------------- 1 | //! Ideally we would write something like this: 2 | //! ``` 3 | //! #[path = "../../../src/bumping.rs"] 4 | //! mod bumping; 5 | //! ``` 6 | //! But rust analyzer cannot handle it, so we copy the file verbatim. 7 | 8 | #[allow(dead_code)] 9 | pub(crate) mod bumping; 10 | 11 | #[allow(dead_code)] 12 | pub(crate) mod chunk_size_config; 13 | -------------------------------------------------------------------------------- /crates/fuzzing-support/src/many_vecs.rs: -------------------------------------------------------------------------------- 1 | use arbitrary::Arbitrary; 2 | use bump_scope::{alloc::Global, Bump, BumpAllocator, BumpVec, MinimumAlignment, SupportedMinimumAlignment}; 3 | use zerocopy::{FromBytes, Immutable, IntoBytes}; 4 | 5 | use crate::MinAlign; 6 | 7 | impl Fuzz { 8 | pub fn run(self) { 9 | if self.up { 10 | self.run_dir::(); 11 | } else { 12 | self.run_dir::(); 13 | } 14 | } 15 | 16 | fn run_dir(self) { 17 | match self.min_align { 18 | MinAlign::Shl0 => self.run_dir_align::(), 19 | MinAlign::Shl1 => self.run_dir_align::(), 20 | MinAlign::Shl2 => self.run_dir_align::(), 21 | MinAlign::Shl3 => self.run_dir_align::(), 22 | MinAlign::Shl4 => self.run_dir_align::(), 23 | } 24 | } 25 | 26 | fn run_dir_align(self) 27 | where 28 | MinimumAlignment: SupportedMinimumAlignment, 29 | { 30 | let bump: Bump = Bump::new(); 31 | 32 | let mut vecs = self 33 | .vecs 34 | .iter() 35 | .enumerate() 36 | .map(|(i, kind)| { 37 | let pattern = (i % 255) as u8; 38 | match kind { 39 | VecKind::T1 => VecObj::new::(&bump, pattern), 40 | VecKind::T2 => VecObj::new::(&bump, pattern), 41 | VecKind::T3 => VecObj::new::(&bump, pattern), 42 | VecKind::T4 => VecObj::new::(&bump, pattern), 43 | VecKind::T5 => VecObj::new::(&bump, pattern), 44 | VecKind::T6 => VecObj::new::(&bump, pattern), 45 | } 46 | }) 47 | .collect::>(); 48 | 49 | let vecs_len = vecs.len(); 50 | 51 | if vecs_len == 0 { 52 | return; 53 | } 54 | 55 | for operation in self.operations { 56 | match operation { 57 | Operation::Push(i) => { 58 | let vec = &mut vecs[i % vecs_len]; 59 | vec.push(); 60 | vec.assert_valid(); 61 | } 62 | } 63 | 64 | for vec in &vecs { 65 | vec.assert_valid(); 66 | } 67 | } 68 | } 69 | } 70 | 71 | #[derive(Debug, Arbitrary)] 72 | pub struct Fuzz { 73 | up: bool, 74 | min_align: MinAlign, 75 | 76 | vecs: Vec, 77 | operations: Vec, 78 | } 79 | 80 | #[derive(Debug, Clone, Copy, Arbitrary)] 81 | enum VecKind { 82 | T1, 83 | T2, 84 | T3, 85 | T4, 86 | T5, 87 | T6, 88 | } 89 | 90 | #[derive(Debug, Arbitrary)] 91 | 92 | enum Operation { 93 | Push(usize), 94 | } 95 | 96 | trait VecTrait { 97 | fn push(&mut self, bit_pattern: u8); 98 | 99 | fn assert_valid(&self, bit_pattern: u8); 100 | } 101 | 102 | struct VecObj<'a> { 103 | vec: Box, 104 | bit_pattern: u8, 105 | } 106 | 107 | impl<'a> VecObj<'a> { 108 | fn new(bump: &'a Bump, bit_pattern: u8) -> Self 109 | where 110 | MinimumAlignment: SupportedMinimumAlignment, 111 | T: Default + FromBytes + IntoBytes + Immutable + 'static, 112 | { 113 | Self { 114 | vec: Box::new(BumpVec::::new_in(bump)), 115 | bit_pattern, 116 | } 117 | } 118 | 119 | fn push(&mut self) { 120 | let Self { vec, bit_pattern } = self; 121 | vec.push(*bit_pattern); 122 | } 123 | 124 | fn assert_valid(&self) { 125 | let Self { vec, bit_pattern } = self; 126 | vec.assert_valid(*bit_pattern); 127 | } 128 | } 129 | 130 | impl VecTrait for BumpVec 131 | where 132 | A: BumpAllocator, 133 | T: Default + FromBytes + IntoBytes + Immutable, 134 | { 135 | fn push(&mut self, bit_pattern: u8) { 136 | self.push(from_bit_pattern(bit_pattern)); 137 | } 138 | 139 | fn assert_valid(&self, bit_pattern: u8) { 140 | for &byte in self.as_slice().as_bytes() { 141 | assert_eq!(byte, bit_pattern); 142 | } 143 | } 144 | } 145 | 146 | fn from_bit_pattern(byte: u8) -> T { 147 | let mut value = T::new_zeroed(); 148 | value.as_mut_bytes().fill(byte); 149 | value 150 | } 151 | 152 | #[derive(Debug, Clone, Copy, Arbitrary)] 153 | enum Align { 154 | T1 = 1 << 0, 155 | T2 = 1 << 1, 156 | T3 = 1 << 2, 157 | T4 = 1 << 3, 158 | T5 = 1 << 4, 159 | T6 = 1 << 5, 160 | } 161 | 162 | #[repr(transparent)] 163 | #[derive(Clone, Default, IntoBytes, FromBytes, Immutable)] 164 | #[allow(dead_code)] 165 | struct T1(u8); 166 | 167 | #[repr(transparent)] 168 | #[derive(Clone, Default, IntoBytes, FromBytes, Immutable)] 169 | #[allow(dead_code)] 170 | struct T2(u16); 171 | 172 | #[repr(transparent)] 173 | #[derive(Clone, Default, IntoBytes, FromBytes, Immutable)] 174 | #[allow(dead_code)] 175 | struct T3(u32); 176 | 177 | #[repr(transparent)] 178 | #[derive(Clone, Default, IntoBytes, FromBytes, Immutable)] 179 | #[allow(dead_code)] 180 | struct T4(u64); 181 | 182 | #[repr(transparent)] 183 | #[derive(Clone, Default, IntoBytes, FromBytes, Immutable)] 184 | #[allow(dead_code)] 185 | struct T5([u64; 2]); 186 | 187 | #[repr(transparent)] 188 | #[derive(Clone, Default, IntoBytes, FromBytes, Immutable)] 189 | #[allow(dead_code)] 190 | struct T6([u64; 3]); 191 | -------------------------------------------------------------------------------- /crates/inspect-asm/README.md: -------------------------------------------------------------------------------- 1 | Moved to . -------------------------------------------------------------------------------- /crates/test-fallibility/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # broken for now :( 3 | # rustflags = "--cfg no_global_oom_handling" -------------------------------------------------------------------------------- /crates/test-fallibility/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /out -------------------------------------------------------------------------------- /crates/test-fallibility/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "allocator-api2" 7 | version = "0.2.21" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 10 | 11 | [[package]] 12 | name = "allocator-api2" 13 | version = "0.3.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "78200ac3468a57d333cd0ea5dd398e25111194dcacd49208afca95c629a6311d" 16 | 17 | [[package]] 18 | name = "bump-scope" 19 | version = "0.17.0" 20 | dependencies = [ 21 | "allocator-api2 0.2.21", 22 | "allocator-api2 0.3.0", 23 | "rustversion", 24 | "zerocopy", 25 | ] 26 | 27 | [[package]] 28 | name = "proc-macro2" 29 | version = "1.0.85" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" 32 | dependencies = [ 33 | "unicode-ident", 34 | ] 35 | 36 | [[package]] 37 | name = "quote" 38 | version = "1.0.36" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 41 | dependencies = [ 42 | "proc-macro2", 43 | ] 44 | 45 | [[package]] 46 | name = "rustversion" 47 | version = "1.0.19" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" 50 | 51 | [[package]] 52 | name = "syn" 53 | version = "2.0.66" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" 56 | dependencies = [ 57 | "proc-macro2", 58 | "quote", 59 | "unicode-ident", 60 | ] 61 | 62 | [[package]] 63 | name = "test-fallibility" 64 | version = "0.0.0" 65 | dependencies = [ 66 | "bump-scope", 67 | ] 68 | 69 | [[package]] 70 | name = "unicode-ident" 71 | version = "1.0.12" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 74 | 75 | [[package]] 76 | name = "zerocopy" 77 | version = "0.8.14" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468" 80 | dependencies = [ 81 | "zerocopy-derive", 82 | ] 83 | 84 | [[package]] 85 | name = "zerocopy-derive" 86 | version = "0.8.14" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1" 89 | dependencies = [ 90 | "proc-macro2", 91 | "quote", 92 | "syn", 93 | ] 94 | -------------------------------------------------------------------------------- /crates/test-fallibility/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-fallibility" 3 | version = "0.0.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | bump-scope = { path = "../..", default-features = false, features = [ 8 | "alloc", 9 | "zerocopy-08", 10 | ] } 11 | -------------------------------------------------------------------------------- /crates/test-fallibility/README.md: -------------------------------------------------------------------------------- 1 | Checks that calling fallible api's does not produce any panicking code. 2 | 3 | This crate also confirms that using `bump-scope` with `![no_std]` works. 4 | -------------------------------------------------------------------------------- /crates/test-fallibility/justfile: -------------------------------------------------------------------------------- 1 | set shell := ["nu", "-c"] 2 | 3 | default: 4 | cargo fmt --all 5 | cargo clippy --all 6 | 7 | save-asm: 8 | @ mkdir out 9 | @ cargo asm --everything --simplify | lines | filter { $in != "" } | str join "\n" | save -f out/everything.asm 10 | 11 | test: save-asm 12 | @ let panics = (open --raw out/everything.asm | find panic | filter { ($in | ansi strip) not-in ['core::panicking::assert_failed:', "call qword ptr [rip + core::panicking::assert_failed_inner@GOTPCREL]"] }); print -e (if ($panics | length) == 0 { "OK" } else { $panics | wrap panics }); exit ($panics | length) 13 | -------------------------------------------------------------------------------- /crates/test-hashbrown/.gitignore: -------------------------------------------------------------------------------- 1 | /target -------------------------------------------------------------------------------- /crates/test-hashbrown/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "allocator-api2" 7 | version = "0.2.21" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 10 | 11 | [[package]] 12 | name = "allocator-api2" 13 | version = "0.3.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "78200ac3468a57d333cd0ea5dd398e25111194dcacd49208afca95c629a6311d" 16 | 17 | [[package]] 18 | name = "bump-scope" 19 | version = "0.17.0" 20 | dependencies = [ 21 | "allocator-api2 0.2.21", 22 | "allocator-api2 0.3.0", 23 | "rustversion", 24 | ] 25 | 26 | [[package]] 27 | name = "equivalent" 28 | version = "1.0.2" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 31 | 32 | [[package]] 33 | name = "foldhash" 34 | version = "0.1.5" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 37 | 38 | [[package]] 39 | name = "hashbrown" 40 | version = "0.15.2" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 43 | dependencies = [ 44 | "allocator-api2 0.2.21", 45 | "equivalent", 46 | "foldhash", 47 | ] 48 | 49 | [[package]] 50 | name = "rustversion" 51 | version = "1.0.20" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" 54 | 55 | [[package]] 56 | name = "test-hashbrown" 57 | version = "0.1.0" 58 | dependencies = [ 59 | "bump-scope", 60 | "hashbrown", 61 | ] 62 | -------------------------------------------------------------------------------- /crates/test-hashbrown/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-hashbrown" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [features] 7 | nightly = ["hashbrown/nightly", "bump-scope/nightly-allocator-api"] 8 | 9 | [dependencies] 10 | bump-scope = { path = "../..", features = ["allocator-api2-02"] } 11 | hashbrown = "0.15.2" 12 | -------------------------------------------------------------------------------- /crates/test-hashbrown/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | use bump_scope::Bump; 4 | use hashbrown::HashMap; 5 | 6 | #[test] 7 | fn test() { 8 | let bump: Bump = Bump::new(); 9 | let mut map = HashMap::new_in(&bump); 10 | map.insert("tau", 6.283); 11 | } 12 | -------------------------------------------------------------------------------- /crates/tests-from-std/.gitignore: -------------------------------------------------------------------------------- 1 | /target -------------------------------------------------------------------------------- /crates/tests-from-std/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "allocator-api2" 7 | version = "0.2.21" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 10 | 11 | [[package]] 12 | name = "allocator-api2" 13 | version = "0.3.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "78200ac3468a57d333cd0ea5dd398e25111194dcacd49208afca95c629a6311d" 16 | 17 | [[package]] 18 | name = "bump-scope" 19 | version = "0.17.0" 20 | dependencies = [ 21 | "allocator-api2 0.2.21", 22 | "allocator-api2 0.3.0", 23 | "rustversion", 24 | ] 25 | 26 | [[package]] 27 | name = "rustversion" 28 | version = "1.0.20" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" 31 | 32 | [[package]] 33 | name = "tests-from-std" 34 | version = "0.1.0" 35 | dependencies = [ 36 | "bump-scope", 37 | ] 38 | -------------------------------------------------------------------------------- /crates/tests-from-std/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tests-from-std" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | bump-scope = { path = "../..", features = [ 8 | "nightly-allocator-api", 9 | "nightly-coerce-unsized", 10 | ] } 11 | -------------------------------------------------------------------------------- /crates/tests-from-std/README.md: -------------------------------------------------------------------------------- 1 | This crate contains tests copied from the rust repository. They make use of nightly features, so running them requires a **nightly compiler**. -------------------------------------------------------------------------------- /crates/tests-from-std/rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Run rustfmt with this config (it should be picked up automatically). 2 | style_edition = "2024" 3 | use_small_heuristics = "Max" 4 | merge_derives = false 5 | group_imports = "StdExternalCrate" 6 | imports_granularity = "Module" 7 | use_field_init_shorthand = true 8 | -------------------------------------------------------------------------------- /crates/tests-from-std/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! These test cases were taken from the rust standard library. 2 | #![cfg(test)] 3 | #![feature(allocator_api, slice_partition_dedup, iter_next_chunk, iter_advance_by)] 4 | #![expect( 5 | clippy::assign_op_pattern, 6 | clippy::byte_char_slices, 7 | clippy::char_lit_as_u8, 8 | clippy::manual_range_patterns, 9 | clippy::non_canonical_clone_impl, 10 | clippy::op_ref, 11 | clippy::redundant_slicing, 12 | clippy::reversed_empty_ranges, 13 | clippy::uninlined_format_args 14 | )] 15 | 16 | mod bump_string; 17 | mod bump_vec; 18 | mod mut_bump_vec; 19 | mod mut_bump_vec_rev; 20 | -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | corpus 3 | artifacts 4 | coverage 5 | -------------------------------------------------------------------------------- /fuzz/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "allocator-api2" 7 | version = "0.2.21" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 10 | 11 | [[package]] 12 | name = "allocator-api2" 13 | version = "0.3.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "78200ac3468a57d333cd0ea5dd398e25111194dcacd49208afca95c629a6311d" 16 | 17 | [[package]] 18 | name = "arbitrary" 19 | version = "1.3.2" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" 22 | dependencies = [ 23 | "derive_arbitrary", 24 | ] 25 | 26 | [[package]] 27 | name = "bump-scope" 28 | version = "0.17.0" 29 | dependencies = [ 30 | "allocator-api2 0.2.21", 31 | "allocator-api2 0.3.0", 32 | "rustversion", 33 | ] 34 | 35 | [[package]] 36 | name = "bump-scope-fuzz" 37 | version = "0.0.0" 38 | dependencies = [ 39 | "fuzzing-support", 40 | "libfuzzer-sys", 41 | ] 42 | 43 | [[package]] 44 | name = "cc" 45 | version = "1.0.83" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" 48 | dependencies = [ 49 | "jobserver", 50 | "libc", 51 | ] 52 | 53 | [[package]] 54 | name = "derive_arbitrary" 55 | version = "1.3.2" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" 58 | dependencies = [ 59 | "proc-macro2", 60 | "quote", 61 | "syn", 62 | ] 63 | 64 | [[package]] 65 | name = "fuzzing-support" 66 | version = "0.0.0" 67 | dependencies = [ 68 | "arbitrary", 69 | "bump-scope", 70 | "rangemap", 71 | "zerocopy", 72 | ] 73 | 74 | [[package]] 75 | name = "jobserver" 76 | version = "0.1.27" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" 79 | dependencies = [ 80 | "libc", 81 | ] 82 | 83 | [[package]] 84 | name = "libc" 85 | version = "0.2.153" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" 88 | 89 | [[package]] 90 | name = "libfuzzer-sys" 91 | version = "0.4.7" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" 94 | dependencies = [ 95 | "arbitrary", 96 | "cc", 97 | "once_cell", 98 | ] 99 | 100 | [[package]] 101 | name = "once_cell" 102 | version = "1.19.0" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 105 | 106 | [[package]] 107 | name = "proc-macro2" 108 | version = "1.0.78" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" 111 | dependencies = [ 112 | "unicode-ident", 113 | ] 114 | 115 | [[package]] 116 | name = "quote" 117 | version = "1.0.35" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 120 | dependencies = [ 121 | "proc-macro2", 122 | ] 123 | 124 | [[package]] 125 | name = "rangemap" 126 | version = "1.5.1" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" 129 | 130 | [[package]] 131 | name = "rustversion" 132 | version = "1.0.19" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" 135 | 136 | [[package]] 137 | name = "syn" 138 | version = "2.0.48" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" 141 | dependencies = [ 142 | "proc-macro2", 143 | "quote", 144 | "unicode-ident", 145 | ] 146 | 147 | [[package]] 148 | name = "unicode-ident" 149 | version = "1.0.12" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 152 | 153 | [[package]] 154 | name = "zerocopy" 155 | version = "0.8.7" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "bb3da5f7220f919a6c7af7c856435a68ee1582fd7a77aa72936257d8335bd6f6" 158 | dependencies = [ 159 | "zerocopy-derive", 160 | ] 161 | 162 | [[package]] 163 | name = "zerocopy-derive" 164 | version = "0.8.7" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "2e5f54f3cc93cd80745404626681b4b9fca9a867bad5a8424b618eb0db1ae6ea" 167 | dependencies = [ 168 | "proc-macro2", 169 | "quote", 170 | "syn", 171 | ] 172 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bump-scope-fuzz" 3 | version = "0.0.0" 4 | publish = false 5 | edition = "2021" 6 | 7 | [package.metadata] 8 | cargo-fuzz = true 9 | 10 | [dependencies] 11 | libfuzzer-sys = "0.4" 12 | fuzzing-support = { path = "../crates/fuzzing-support" } 13 | 14 | [[bin]] 15 | name = "bumping" 16 | path = "fuzz_targets/bumping.rs" 17 | test = false 18 | doc = false 19 | bench = false 20 | 21 | [[bin]] 22 | name = "chunk_size" 23 | path = "fuzz_targets/chunk_size.rs" 24 | test = false 25 | doc = false 26 | bench = false 27 | 28 | [[bin]] 29 | name = "allocator" 30 | path = "fuzz_targets/allocator.rs" 31 | test = false 32 | doc = false 33 | bench = false 34 | 35 | [[bin]] 36 | name = "bump_up" 37 | path = "fuzz_targets/bump_up.rs" 38 | test = false 39 | doc = false 40 | bench = false 41 | 42 | [[bin]] 43 | name = "bump_down" 44 | path = "fuzz_targets/bump_down.rs" 45 | test = false 46 | doc = false 47 | bench = false 48 | 49 | [[bin]] 50 | name = "bump_greedy_up" 51 | path = "fuzz_targets/bump_greedy_up.rs" 52 | test = false 53 | doc = false 54 | bench = false 55 | 56 | [[bin]] 57 | name = "bump_greedy_down" 58 | path = "fuzz_targets/bump_greedy_down.rs" 59 | test = false 60 | doc = false 61 | bench = false 62 | 63 | [[bin]] 64 | name = "many_vecs" 65 | path = "fuzz_targets/many_vecs.rs" 66 | test = false 67 | doc = false 68 | bench = false 69 | 70 | [[bin]] 71 | name = "slice_split_off" 72 | path = "fuzz_targets/slice_split_off.rs" 73 | test = false 74 | doc = false 75 | bench = false 76 | 77 | [[bin]] 78 | name = "vec_split_off" 79 | path = "fuzz_targets/vec_split_off.rs" 80 | test = false 81 | doc = false 82 | bench = false 83 | -------------------------------------------------------------------------------- /fuzz/allocator-cov.nu: -------------------------------------------------------------------------------- 1 | def main [ 2 | format?: string, 3 | ] { 4 | let bump_scope = $env.FILE_PWD | path dirname 5 | let target = "allocator" 6 | 7 | mut flags = [ 8 | target/x86_64-unknown-linux-gnu/coverage/x86_64-unknown-linux-gnu/release/($target) 9 | --instr-profile=coverage/($target)/coverage.profdata 10 | --Xdemangler=rustfilt 11 | --ignore-filename-regex=cargo/registry 12 | 13 | $"($bump_scope)/src/allocator.rs" 14 | ] 15 | 16 | if $format == "html" { 17 | $flags ++= [ 18 | --format=html 19 | ] 20 | 21 | let page_path = $"coverage/($target)/index.html" 22 | ^cargo cov -- show ...$flags | save -f $page_path 23 | } else { 24 | $flags ++= [ 25 | --use-color 26 | ] 27 | 28 | print $flags 29 | cargo cov -- show ...$flags 30 | } 31 | } -------------------------------------------------------------------------------- /fuzz/fuzz_targets/allocator.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use fuzzing_support::allocator_api::Fuzz; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | fuzz_target!(|fuzz: Fuzz| fuzz.run()); 7 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/bump_down.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use fuzzing_support::bump_down::Fuzz; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | fuzz_target!(|fuzz: Fuzz| fuzz.run()); 7 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/bump_greedy_down.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use fuzzing_support::bump_prepare_down::Fuzz; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | fuzz_target!(|fuzz: Fuzz| fuzz.run()); 7 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/bump_greedy_up.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use fuzzing_support::bump_prepare_up::Fuzz; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | fuzz_target!(|fuzz: Fuzz| fuzz.run()); 7 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/bump_up.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use fuzzing_support::bump_up::Fuzz; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | fuzz_target!(|fuzz: Fuzz| fuzz.run()); 7 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/bumping.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use fuzzing_support::bumping::Fuzz; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | fuzz_target!(|fuzz: Fuzz| fuzz.run()); 7 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/chunk_size.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use fuzzing_support::chunk_size::Fuzz; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | fuzz_target!(|fuzz: Fuzz| fuzz.run()); 7 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/many_vecs.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use fuzzing_support::many_vecs::Fuzz; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | fuzz_target!(|fuzz: Fuzz| fuzz.run()); 7 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/slice_split_off.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use core::{mem, ops::Range}; 4 | 5 | use fuzzing_support::bump_scope::Bump; 6 | use libfuzzer_sys::{ 7 | arbitrary::{Arbitrary, Unstructured}, 8 | fuzz_target, 9 | }; 10 | 11 | fuzz_target!(|fuzz: Fuzz| fuzz.run()); 12 | 13 | #[derive(Debug)] 14 | struct Fuzz { 15 | len: usize, 16 | range: Range, 17 | } 18 | 19 | impl<'a> Arbitrary<'a> for Fuzz { 20 | fn arbitrary(u: &mut Unstructured<'a>) -> libfuzzer_sys::arbitrary::Result { 21 | let len = u.int_in_range(0..=10)?; 22 | let mut start = u.int_in_range(0..=len)?; 23 | let mut end = u.int_in_range(0..=len)?; 24 | 25 | if start > end { 26 | mem::swap(&mut start, &mut end); 27 | } 28 | 29 | Ok(Fuzz { len, range: start..end }) 30 | } 31 | } 32 | 33 | impl Fuzz { 34 | fn run(self) { 35 | let bump: Bump = Bump::new(); 36 | let mut slice = bump.alloc_iter(0..self.len); 37 | 38 | let expected_removed = (&*slice)[self.range.clone()].to_vec(); 39 | let mut expected_remaining = (&*slice)[..self.range.start].to_vec(); 40 | expected_remaining.extend((&*slice)[self.range.end..].iter().copied()); 41 | 42 | let removed = slice.split_off(self.range); 43 | 44 | assert_eq!(&*expected_removed, &*removed); 45 | assert_eq!(&*slice, &*expected_remaining); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/vec_split_off.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use core::{mem, ops::Range}; 4 | 5 | use fuzzing_support::bump_scope::{Bump, BumpVec}; 6 | use libfuzzer_sys::{ 7 | arbitrary::{Arbitrary, Unstructured}, 8 | fuzz_target, 9 | }; 10 | 11 | fuzz_target!(|fuzz: Fuzz| fuzz.run()); 12 | 13 | #[derive(Debug)] 14 | struct Fuzz { 15 | len: usize, 16 | range: Range, 17 | } 18 | 19 | impl<'a> Arbitrary<'a> for Fuzz { 20 | fn arbitrary(u: &mut Unstructured<'a>) -> libfuzzer_sys::arbitrary::Result { 21 | let len = u.int_in_range(0..=10)?; 22 | let mut start = u.int_in_range(0..=len)?; 23 | let mut end = u.int_in_range(0..=len)?; 24 | 25 | if start > end { 26 | mem::swap(&mut start, &mut end); 27 | } 28 | 29 | Ok(Fuzz { len, range: start..end }) 30 | } 31 | } 32 | 33 | impl Fuzz { 34 | fn run(self) { 35 | let bump: Bump = Bump::new(); 36 | let mut vec = BumpVec::with_capacity_in(200, &bump); 37 | vec.extend(0..self.len); 38 | 39 | let expected_removed = vec[self.range.clone()].to_vec(); 40 | let mut expected_remaining = vec[..self.range.start].to_vec(); 41 | expected_remaining.extend(vec[self.range.end..].iter().copied()); 42 | 43 | let original_addr = vec.as_ptr() as usize; 44 | let removed = vec.split_off(self.range); 45 | 46 | assert_eq!(&*expected_removed, &*removed); 47 | assert_eq!(&*vec, &*expected_remaining); 48 | 49 | if vec.as_ptr() as usize == original_addr { 50 | if vec.len() < self.len { 51 | assert_eq!(vec.capacity(), vec.len()); 52 | } 53 | 54 | assert_eq!(removed.capacity(), 200 - vec.capacity()); 55 | } else { 56 | assert_eq!(removed.as_ptr() as usize, original_addr); 57 | 58 | if removed.len() < self.len { 59 | assert_eq!(removed.capacity(), removed.len()); 60 | } 61 | 62 | assert_eq!(vec.capacity(), 200 - removed.capacity()); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /fuzz/justfile: -------------------------------------------------------------------------------- 1 | set shell := ["nu", "-c"] 2 | 3 | default *args: 4 | cargo fmt --all 5 | cargo clippy --all -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | # This was written for nushell version 0.100.0 2 | set shell := ["nu", "-c"] 3 | 4 | export RUST_BACKTRACE := "1" 5 | export MIRIFLAGS := "-Zmiri-strict-provenance" 6 | 7 | default: 8 | @just --list 9 | 10 | pre-release: 11 | just spellcheck 12 | just doc 13 | just check 14 | just test 15 | cargo +stable semver-checks 16 | 17 | check: 18 | just check-fmt 19 | just check-clippy 20 | just check-nostd 21 | just check-fallibility 22 | # regression test making sure hashbrown compiles 23 | cargo check --tests --features nightly-allocator-api 24 | 25 | check-fmt: 26 | cargo fmt --check 27 | cd crates/fuzzing-support; cargo fmt --check 28 | cd crates/test-fallibility; cargo fmt --check 29 | cd fuzz; cargo fmt --check 30 | 31 | check-clippy: 32 | # TODO: add "allocator-api2-03" once it got a new release that makes its "alloc" feature msrv compliant 33 | cargo +1.64.0 check --no-default-features 34 | cargo +1.64.0 check --features serde,zerocopy-08,allocator-api2-02 35 | 36 | cargo +stable clippy --tests --no-default-features 37 | cargo +stable clippy --tests --features serde,zerocopy-08,allocator-api2-02,allocator-api2-03 38 | 39 | cargo +nightly clippy --tests --no-default-features 40 | cargo +nightly clippy --tests --features serde,zerocopy-08,allocator-api2-02,allocator-api2-03 41 | cargo +nightly clippy --tests --all-features 42 | 43 | cd crates/fuzzing-support; cargo clippy --tests 44 | cd crates/test-fallibility; cargo clippy --tests 45 | cd crates/tests-from-std; cargo clippy --tests 46 | cd fuzz; cargo clippy 47 | 48 | check-nostd: 49 | cd crates/test-fallibility; cargo check 50 | 51 | check-fallibility: 52 | @ just crates/test-fallibility/test 53 | 54 | test: 55 | just test-non-miri 56 | just test-miri 57 | 58 | test-non-miri: 59 | cargo test --all-features 60 | cd crates/tests-from-std; cargo test 61 | cd crates/test-hashbrown; cargo test 62 | cd crates/test-hashbrown; cargo test --all-features 63 | cd crates/fuzzing-support; cargo test 64 | 65 | test-miri: 66 | cargo miri test --all-features 67 | cd crates/tests-from-std; cargo miri test 68 | cd crates/test-hashbrown; cargo miri test 69 | cd crates/test-hashbrown; cargo miri test --all-features 70 | cd crates/fuzzing-support; cargo miri test 71 | 72 | fmt: 73 | cargo fmt 74 | cd crates/fuzzing-support; cargo fmt 75 | cd crates/test-fallibility; cargo fmt 76 | cd fuzz; cargo fmt 77 | 78 | spellcheck: 79 | # https://www.npmjs.com/package/cspell 80 | cspell lint --gitignore "**/*.{rs,md,toml}" --exclude crates/tests-from-std 81 | 82 | doc *args: 83 | cargo test --package bump-scope --lib --all-features -- insert_feature_docs --exact --ignored 84 | cargo fmt 85 | @ just doc-rustdoc {{args}} 86 | @# https://github.com/orium/cargo-rdme 87 | @# TODO(blocked): stop stripping links when is merged 88 | cargo rdme --force --intralinks-strip-links 89 | 90 | doc-rustdoc *args: 91 | cargo rustdoc {{args}} --all-features -- --cfg docsrs -Z unstable-options --generate-link-to-definition 92 | 93 | doc-rustdoc-priv *args: 94 | cargo rustdoc {{args}} --all-features -- --cfg docsrs -Z unstable-options --generate-link-to-definition --document-private-items -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 125 2 | imports_granularity = "Crate" -------------------------------------------------------------------------------- /src/bump_align_guard.rs: -------------------------------------------------------------------------------- 1 | use crate::{BaseAllocator, BumpScope, MinimumAlignment, SupportedMinimumAlignment}; 2 | 3 | /// Aligns the bump pointer on drop. 4 | /// 5 | /// This is useful in unsafe contexts where the alignment is changed and we have to change it back. 6 | /// The `BumpScope` is in an invalid state when the bump pointer alignment does not match `MIN_ALIGN`. 7 | /// So `drop` ***must*** be called to return the bump scope to a valid state. 8 | pub(crate) struct BumpAlignGuard<'b, 'a, A, const MIN_ALIGN: usize, const UP: bool, const GUARANTEED_ALLOCATED: bool> 9 | where 10 | MinimumAlignment: SupportedMinimumAlignment, 11 | A: BaseAllocator, 12 | { 13 | pub(crate) scope: &'b mut BumpScope<'a, A, MIN_ALIGN, UP, GUARANTEED_ALLOCATED>, 14 | } 15 | 16 | impl Drop 17 | for BumpAlignGuard<'_, '_, A, MIN_ALIGN, UP, GUARANTEED_ALLOCATED> 18 | where 19 | MinimumAlignment: SupportedMinimumAlignment, 20 | A: BaseAllocator, 21 | { 22 | #[inline(always)] 23 | fn drop(&mut self) { 24 | self.scope.chunk.get().align_pos_to::(); 25 | } 26 | } 27 | 28 | impl<'b, 'a, A, const MIN_ALIGN: usize, const UP: bool, const GUARANTEED_ALLOCATED: bool> 29 | BumpAlignGuard<'b, 'a, A, MIN_ALIGN, UP, GUARANTEED_ALLOCATED> 30 | where 31 | MinimumAlignment: SupportedMinimumAlignment, 32 | A: BaseAllocator, 33 | { 34 | #[inline(always)] 35 | pub(crate) fn new(scope: &'b mut BumpScope<'a, A, MIN_ALIGN, UP, GUARANTEED_ALLOCATED>) -> Self { 36 | Self { scope } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/bump_allocator_scope.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | BaseAllocator, Bump, BumpAllocator, BumpScope, MinimumAlignment, SupportedMinimumAlignment, WithoutDealloc, 3 | WithoutShrink, 4 | }; 5 | 6 | /// An allocator that makes allocations with a lifetime of `'a`. 7 | /// 8 | /// # Safety 9 | /// 10 | /// This trait must only be implemented when allocations live for `'a`. 11 | /// For example this function must be sound: 12 | /// 13 | /// ``` 14 | /// # #![allow(dead_code)] 15 | /// use bump_scope::BumpAllocatorScope; 16 | /// use core::alloc::Layout; 17 | /// 18 | /// fn allocate_zeroed_bytes<'a>(allocator: impl BumpAllocatorScope<'a>, len: usize) -> &'a [u8] { 19 | /// let layout = Layout::array::(len).unwrap(); 20 | /// let ptr = allocator.allocate_zeroed(layout).unwrap(); 21 | /// unsafe { ptr.as_ref() } 22 | /// } 23 | /// ``` 24 | pub unsafe trait BumpAllocatorScope<'a>: BumpAllocator { 25 | // TODO: implement `stats` that live for `'a`? 26 | } 27 | 28 | unsafe impl<'a, A: BumpAllocatorScope<'a>> BumpAllocatorScope<'a> for &A {} 29 | unsafe impl<'a, A: BumpAllocatorScope<'a>> BumpAllocatorScope<'a> for WithoutDealloc {} 30 | unsafe impl<'a, A: BumpAllocatorScope<'a>> BumpAllocatorScope<'a> for WithoutShrink {} 31 | 32 | unsafe impl<'a, A, const MIN_ALIGN: usize, const UP: bool, const GUARANTEED_ALLOCATED: bool> BumpAllocatorScope<'a> 33 | for BumpScope<'a, A, MIN_ALIGN, UP, GUARANTEED_ALLOCATED> 34 | where 35 | MinimumAlignment: SupportedMinimumAlignment, 36 | A: BaseAllocator, 37 | { 38 | } 39 | 40 | unsafe impl<'a, A, const MIN_ALIGN: usize, const UP: bool, const GUARANTEED_ALLOCATED: bool> BumpAllocatorScope<'a> 41 | for &'a Bump 42 | where 43 | MinimumAlignment: SupportedMinimumAlignment, 44 | A: BaseAllocator, 45 | { 46 | } 47 | 48 | unsafe impl<'a, A, const MIN_ALIGN: usize, const UP: bool, const GUARANTEED_ALLOCATED: bool> BumpAllocatorScope<'a> 49 | for &mut BumpScope<'a, A, MIN_ALIGN, UP, GUARANTEED_ALLOCATED> 50 | where 51 | MinimumAlignment: SupportedMinimumAlignment, 52 | A: BaseAllocator, 53 | { 54 | } 55 | 56 | unsafe impl<'a, A, const MIN_ALIGN: usize, const UP: bool, const GUARANTEED_ALLOCATED: bool> BumpAllocatorScope<'a> 57 | for &'a mut Bump 58 | where 59 | MinimumAlignment: SupportedMinimumAlignment, 60 | A: BaseAllocator, 61 | { 62 | } 63 | -------------------------------------------------------------------------------- /src/bump_box/slice_initializer.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | marker::PhantomData, 3 | mem::{self, ManuallyDrop, MaybeUninit}, 4 | ptr::NonNull, 5 | }; 6 | 7 | use crate::{ 8 | polyfill::{non_null, pointer}, 9 | BumpBox, SizedTypeProperties, 10 | }; 11 | 12 | /// Allows for initializing a `BumpBox<[MaybeUninit]>` by pushing values. 13 | /// On drop, this will drop the values that have been pushed so far. 14 | pub(crate) struct BumpBoxSliceInitializer<'a, T> { 15 | pos: NonNull, 16 | 17 | start: NonNull, 18 | end: NonNull, // if T is a ZST this is ptr + len 19 | 20 | /// First field marks the lifetime. 21 | /// Second field marks ownership over T. () 22 | marker: PhantomData<(&'a (), T)>, 23 | } 24 | 25 | impl Drop for BumpBoxSliceInitializer<'_, T> { 26 | fn drop(&mut self) { 27 | unsafe { 28 | let to_drop_len = self.init_len(); 29 | let to_drop = non_null::slice_from_raw_parts(self.start, to_drop_len); 30 | to_drop.as_ptr().drop_in_place(); 31 | } 32 | } 33 | } 34 | 35 | impl<'a, T> BumpBoxSliceInitializer<'a, T> { 36 | #[inline(always)] 37 | pub(crate) fn new(slice: BumpBox<'a, [MaybeUninit]>) -> Self { 38 | if T::IS_ZST { 39 | let start = NonNull::dangling(); 40 | let end = unsafe { non_null::wrapping_byte_add(start, slice.len()) }; 41 | 42 | return Self { 43 | pos: start, 44 | start, 45 | end, 46 | marker: PhantomData, 47 | }; 48 | } 49 | 50 | let len = slice.len(); 51 | let slice = slice.into_raw(); 52 | 53 | unsafe { 54 | let start = slice.cast::(); 55 | let end = non_null::add(start, len); 56 | 57 | Self { 58 | pos: start, 59 | start, 60 | end, 61 | marker: PhantomData, 62 | } 63 | } 64 | } 65 | 66 | #[inline(always)] 67 | fn init_len(&self) -> usize { 68 | if T::IS_ZST { 69 | non_null::addr(self.pos).get().wrapping_sub(non_null::addr(self.start).get()) 70 | } else { 71 | unsafe { non_null::offset_from_unsigned(self.pos, self.start) } 72 | } 73 | } 74 | 75 | #[inline(always)] 76 | fn len(&self) -> usize { 77 | if T::IS_ZST { 78 | non_null::addr(self.end).get().wrapping_sub(non_null::addr(self.start).get()) 79 | } else { 80 | unsafe { non_null::offset_from_unsigned(self.end, self.start) } 81 | } 82 | } 83 | 84 | #[inline(always)] 85 | pub(crate) fn is_full(&self) -> bool { 86 | self.pos == self.end 87 | } 88 | 89 | #[inline(always)] 90 | pub(crate) fn push(&mut self, value: T) { 91 | self.push_with(|| value); 92 | } 93 | 94 | #[inline(always)] 95 | pub(crate) fn push_with(&mut self, f: impl FnOnce() -> T) { 96 | assert!(!self.is_full()); 97 | unsafe { self.push_with_unchecked(f) } 98 | } 99 | 100 | #[inline(always)] 101 | pub(crate) unsafe fn push_unchecked(&mut self, value: T) { 102 | self.push_with_unchecked(|| value); 103 | } 104 | 105 | #[inline(always)] 106 | pub(crate) unsafe fn push_with_unchecked(&mut self, f: impl FnOnce() -> T) { 107 | debug_assert!(!self.is_full()); 108 | 109 | if T::IS_ZST { 110 | mem::forget(f()); 111 | self.pos = non_null::wrapping_byte_add(self.pos, 1); 112 | } else { 113 | pointer::write_with(self.pos.as_ptr(), f); 114 | self.pos = non_null::add(self.pos, 1); 115 | } 116 | } 117 | 118 | #[inline(always)] 119 | pub(crate) fn into_init(self) -> BumpBox<'a, [T]> { 120 | assert!(self.is_full()); 121 | unsafe { self.into_init_unchecked() } 122 | } 123 | 124 | #[inline(always)] 125 | pub(crate) unsafe fn into_init_unchecked(self) -> BumpBox<'a, [T]> { 126 | let this = ManuallyDrop::new(self); 127 | debug_assert!(this.is_full()); 128 | let len = this.len(); 129 | let slice = non_null::slice_from_raw_parts(this.start, len); 130 | BumpBox::from_raw(slice) 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/bump_vec/drain.rs: -------------------------------------------------------------------------------- 1 | //! This is not part of public api. 2 | //! 3 | //! This exists solely for the implementation of [`Splice`](crate::bump_vec::Splice). 4 | 5 | use core::{ 6 | fmt::{self, Debug}, 7 | iter::FusedIterator, 8 | mem, 9 | ptr::{self, NonNull}, 10 | slice::{self}, 11 | }; 12 | 13 | use crate::{BumpAllocator, BumpVec, SizedTypeProperties}; 14 | 15 | /// A draining iterator for `BumpVec`. 16 | /// 17 | /// This `struct` is not directly created by any method. 18 | /// It is an implementation detail of `Splice`. 19 | /// 20 | /// The implementation of `drain` does not need a pointer to the whole `BumpVec` 21 | /// (and all the generic parameters that come with it). 22 | /// That's why we return `owned_slice::Drain` from `BumpVec::drain`. 23 | /// 24 | /// However just like the standard library we use a `Drain` implementation to implement 25 | /// `Splice`. For this particular case we *do* need a pointer to `BumpVec`. 26 | pub struct Drain<'a, T: 'a, A: BumpAllocator> { 27 | /// Index of tail to preserve 28 | pub(super) tail_start: usize, 29 | /// Length of tail 30 | pub(super) tail_len: usize, 31 | /// Current remaining range to remove 32 | pub(super) iter: slice::Iter<'a, T>, 33 | pub(super) vec: NonNull>, 34 | } 35 | 36 | impl Debug for Drain<'_, T, A> { 37 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 38 | f.debug_tuple("Drain").field(&self.iter.as_slice()).finish() 39 | } 40 | } 41 | 42 | impl Drain<'_, T, A> { 43 | /// Returns the remaining items of this iterator as a slice. 44 | /// 45 | /// # Examples 46 | /// 47 | /// ``` 48 | /// let mut vec = vec!['a', 'b', 'c']; 49 | /// let mut drain = vec.drain(..); 50 | /// assert_eq!(drain.as_slice(), &['a', 'b', 'c']); 51 | /// let _ = drain.next().unwrap(); 52 | /// assert_eq!(drain.as_slice(), &['b', 'c']); 53 | /// ``` 54 | #[must_use] 55 | #[inline(always)] 56 | pub fn as_slice(&self) -> &[T] { 57 | self.iter.as_slice() 58 | } 59 | } 60 | 61 | impl AsRef<[T]> for Drain<'_, T, A> { 62 | #[inline(always)] 63 | fn as_ref(&self) -> &[T] { 64 | self.as_slice() 65 | } 66 | } 67 | 68 | impl Iterator for Drain<'_, T, A> { 69 | type Item = T; 70 | 71 | #[inline(always)] 72 | fn next(&mut self) -> Option { 73 | self.iter.next().map(|elt| unsafe { ptr::read(elt as *const _) }) 74 | } 75 | 76 | #[inline(always)] 77 | fn size_hint(&self) -> (usize, Option) { 78 | self.iter.size_hint() 79 | } 80 | } 81 | 82 | impl DoubleEndedIterator for Drain<'_, T, A> { 83 | #[inline(always)] 84 | fn next_back(&mut self) -> Option { 85 | self.iter.next_back().map(|elt| unsafe { ptr::read(elt as *const _) }) 86 | } 87 | } 88 | 89 | impl Drop for Drain<'_, T, A> { 90 | #[inline] 91 | fn drop(&mut self) { 92 | /// Moves back the un-`Drain`ed elements to restore the original vector. 93 | struct DropGuard<'r, 'a, T, A: BumpAllocator>(&'r mut Drain<'a, T, A>); 94 | 95 | impl Drop for DropGuard<'_, '_, T, A> { 96 | fn drop(&mut self) { 97 | if self.0.tail_len > 0 { 98 | unsafe { 99 | let source_vec = self.0.vec.as_mut(); 100 | // memmove back untouched tail, update to new length 101 | let start = source_vec.len(); 102 | let tail = self.0.tail_start; 103 | if tail != start { 104 | let src = source_vec.as_ptr().add(tail); 105 | let dst = source_vec.as_mut_ptr().add(start); 106 | ptr::copy(src, dst, self.0.tail_len); 107 | } 108 | source_vec.set_len(start + self.0.tail_len); 109 | } 110 | } 111 | } 112 | } 113 | 114 | let iter = mem::replace(&mut self.iter, [].iter()); 115 | let drop_len = iter.len(); 116 | 117 | let mut vec = self.vec; 118 | 119 | if T::IS_ZST { 120 | // ZSTs have no identity, so we don't need to move them around, we only need to drop the correct amount. 121 | // this can be achieved by manipulating the vector length instead of moving values out from `iter`. 122 | unsafe { 123 | let vec = vec.as_mut(); 124 | let old_len = vec.len(); 125 | vec.set_len(old_len + drop_len + self.tail_len); 126 | vec.truncate(old_len + self.tail_len); 127 | } 128 | 129 | return; 130 | } 131 | 132 | // ensure elements are moved back into their appropriate places, even when drop_in_place panics 133 | let _guard = DropGuard(self); 134 | 135 | if drop_len == 0 { 136 | return; 137 | } 138 | 139 | // as_slice() must only be called when iter.len() is > 0 because 140 | // vec::Splice modifies vec::Drain fields and may grow the vec which would invalidate 141 | // the iterator's internal pointers. Creating a reference to deallocated memory 142 | // is invalid even when it is zero-length 143 | let drop_ptr = iter.as_slice().as_ptr(); 144 | 145 | #[allow(clippy::cast_sign_loss)] 146 | unsafe { 147 | // drop_ptr comes from a slice::Iter which only gives us a &[T] but for drop_in_place 148 | // a pointer with mutable provenance is necessary. Therefore we must reconstruct 149 | // it from the original vec but also avoid creating a &mut to the front since that could 150 | // invalidate raw pointers to it which some unsafe code might rely on. 151 | let vec_ptr = vec.as_mut().as_mut_ptr(); 152 | let drop_offset = drop_ptr.offset_from(vec_ptr) as usize; 153 | let to_drop = ptr::slice_from_raw_parts_mut(vec_ptr.add(drop_offset), drop_len); 154 | ptr::drop_in_place(to_drop); 155 | } 156 | } 157 | } 158 | 159 | impl ExactSizeIterator for Drain<'_, T, A> {} 160 | 161 | impl FusedIterator for Drain<'_, T, A> {} 162 | -------------------------------------------------------------------------------- /src/bump_vec/splice.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "panic-on-alloc")] 2 | 3 | use core::{ptr, slice}; 4 | 5 | use crate::{destructure::destructure, BumpAllocator, BumpVec}; 6 | 7 | use super::Drain; 8 | 9 | /// A splicing iterator for `BumpVec`. 10 | /// 11 | /// This struct is created by [`BumpVec::splice()`]. 12 | /// See its documentation for more. 13 | /// 14 | /// # Example 15 | /// 16 | /// ``` 17 | /// # use bump_scope::{Bump, bump_vec}; 18 | /// # let bump: Bump = Bump::new(); 19 | /// let mut v = bump_vec![in ≎ 0, 1, 2]; 20 | /// let new = [7, 8]; 21 | /// let old = bump.alloc_iter_exact(v.splice(1.., new)); 22 | /// assert_eq!(old, [1, 2]); 23 | /// assert_eq!(v, [0, 7, 8]); 24 | /// ``` 25 | /// 26 | /// [`BumpVec::splice()`]: crate::BumpVec::splice 27 | #[derive(Debug)] 28 | pub struct Splice<'a, I: Iterator + 'a, A: BumpAllocator> { 29 | pub(super) drain: Drain<'a, I::Item, A>, 30 | pub(super) replace_with: I, 31 | } 32 | 33 | impl Iterator for Splice<'_, I, A> { 34 | type Item = I::Item; 35 | 36 | #[inline] 37 | fn next(&mut self) -> Option { 38 | self.drain.next() 39 | } 40 | 41 | #[inline] 42 | fn size_hint(&self) -> (usize, Option) { 43 | self.drain.size_hint() 44 | } 45 | } 46 | 47 | impl DoubleEndedIterator for Splice<'_, I, A> { 48 | #[inline] 49 | fn next_back(&mut self) -> Option { 50 | self.drain.next_back() 51 | } 52 | } 53 | 54 | impl ExactSizeIterator for Splice<'_, I, A> {} 55 | 56 | impl Drop for Splice<'_, I, A> { 57 | fn drop(&mut self) { 58 | self.drain.by_ref().for_each(drop); 59 | // At this point draining is done and the only remaining tasks are splicing 60 | // and moving things into the final place. 61 | // Which means we can replace the slice::Iter with pointers that won't point to deallocated 62 | // memory, so that Drain::drop is still allowed to call iter.len(), otherwise it would break 63 | // the ptr.sub_ptr contract. 64 | self.drain.iter = <[I::Item]>::iter(&[]); 65 | 66 | unsafe { 67 | if self.drain.tail_len == 0 { 68 | self.drain.vec.as_mut().extend(self.replace_with.by_ref()); 69 | return; 70 | } 71 | 72 | // First fill the range left by drain(). 73 | if !self.drain.fill(&mut self.replace_with) { 74 | return; 75 | } 76 | 77 | // There may be more elements. Use the lower bound as an estimate. 78 | // STD-FIXME: Is the upper bound a better guess? Or something else? 79 | let (lower_bound, _upper_bound) = self.replace_with.size_hint(); 80 | if lower_bound > 0 { 81 | self.drain.move_tail(lower_bound); 82 | if !self.drain.fill(&mut self.replace_with) { 83 | return; 84 | } 85 | } 86 | 87 | // Collect any remaining elements. 88 | // This is a zero-length vector which does not allocate if `lower_bound` was exact. 89 | let collected = BumpVec::from_iter_in(&mut self.replace_with, self.drain.vec.as_ref().allocator()); 90 | 91 | // We can't use `into_fixed_vec` here because that would require a 92 | // `BumpAllocatorScope<'a>` instead of just a `BumpAllocator`. 93 | destructure!(let BumpVec:: { fixed: collected } = collected); 94 | let mut collected = collected.cook().into_iter(); 95 | 96 | // Now we have an exact count. 97 | #[allow(clippy::len_zero)] 98 | if collected.len() > 0 { 99 | self.drain.move_tail(collected.len()); 100 | let filled = self.drain.fill(&mut collected); 101 | debug_assert!(filled); 102 | debug_assert_eq!(collected.len(), 0); 103 | } 104 | } 105 | // Let `Drain::drop` move the tail back if necessary and restore `vec.len`. 106 | } 107 | } 108 | 109 | /// Private helper methods for `Splice::drop` 110 | impl Drain<'_, T, A> { 111 | /// The range from `self.vec.len` to `self.tail_start` contains elements 112 | /// that have been moved out. 113 | /// Fill that range as much as possible with new elements from the `replace_with` iterator. 114 | /// Returns `true` if we filled the entire range. (`replace_with.next()` didn’t return `None`.) 115 | unsafe fn fill>(&mut self, replace_with: &mut I) -> bool { 116 | let vec = self.vec.as_mut(); 117 | let range_start = vec.len(); 118 | let range_end = self.tail_start; 119 | let range_slice = slice::from_raw_parts_mut(vec.as_mut_ptr().add(range_start), range_end - range_start); 120 | 121 | for place in range_slice { 122 | if let Some(new_item) = replace_with.next() { 123 | ptr::write(place, new_item); 124 | vec.inc_len(1); 125 | } else { 126 | return false; 127 | } 128 | } 129 | true 130 | } 131 | 132 | /// Makes room for inserting more elements before the tail. 133 | unsafe fn move_tail(&mut self, additional: usize) { 134 | let vec = self.vec.as_mut(); 135 | let len = self.tail_start + self.tail_len; 136 | vec.buf_reserve(len, additional); 137 | 138 | let new_tail_start = self.tail_start + additional; 139 | 140 | let src = vec.as_ptr().add(self.tail_start); 141 | let dst = vec.as_mut_ptr().add(new_tail_start); 142 | ptr::copy(src, dst, self.tail_len); 143 | 144 | self.tail_start = new_tail_start; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/chunk_header.rs: -------------------------------------------------------------------------------- 1 | use core::{cell::Cell, ptr::NonNull}; 2 | 3 | use crate::polyfill::non_null; 4 | 5 | #[repr(C, align(16))] 6 | pub(crate) struct ChunkHeader { 7 | pub(crate) pos: Cell>, 8 | pub(crate) end: NonNull, 9 | 10 | pub(crate) prev: Option>, 11 | pub(crate) next: Cell>>, 12 | 13 | pub(crate) allocator: A, 14 | } 15 | 16 | /// Wraps a [`ChunkHeader`], making it Sync so it can be used as a static. 17 | /// The empty chunk is never mutated, so this is fine. 18 | struct UnallocatedChunkHeader(ChunkHeader); 19 | 20 | unsafe impl Sync for UnallocatedChunkHeader {} 21 | 22 | static UNALLOCATED_CHUNK_HEADER: UnallocatedChunkHeader = UnallocatedChunkHeader(ChunkHeader { 23 | pos: Cell::new(NonNull::::dangling().cast()), 24 | end: NonNull::::dangling().cast(), 25 | prev: None, 26 | next: Cell::new(None), 27 | allocator: (), 28 | }); 29 | 30 | #[rustversion::attr(since(1.83), const)] 31 | pub(crate) fn unallocated_chunk_header() -> NonNull { 32 | non_null::from_ref(&UNALLOCATED_CHUNK_HEADER.0) 33 | } 34 | -------------------------------------------------------------------------------- /src/chunk_size.rs: -------------------------------------------------------------------------------- 1 | use core::{alloc::Layout, marker::PhantomData, num::NonZeroUsize}; 2 | 3 | use chunk_size_config::ChunkSizeConfig; 4 | 5 | use crate::{polyfill::option::const_unwrap, ChunkHeader}; 6 | 7 | mod chunk_size_config; 8 | 9 | const _: () = assert!(chunk_size_config::MIN_CHUNK_ALIGN == crate::bumping::MIN_CHUNK_ALIGN); 10 | 11 | /// We leave some space per allocation for the base allocator. 12 | pub(crate) type AssumedMallocOverhead = [*const u8; 2]; 13 | 14 | pub const fn config() -> ChunkSizeConfig { 15 | ChunkSizeConfig { 16 | up: UP, 17 | assumed_malloc_overhead_layout: Layout::new::(), 18 | chunk_header_layout: Layout::new::>(), 19 | } 20 | } 21 | 22 | macro_rules! attempt { 23 | ($expr:expr) => { 24 | match $expr { 25 | Some(some) => some, 26 | None => return None, 27 | } 28 | }; 29 | } 30 | 31 | pub struct ChunkSize { 32 | size: NonZeroUsize, 33 | marker: PhantomData<*const A>, 34 | } 35 | 36 | impl Clone for ChunkSize { 37 | fn clone(&self) -> Self { 38 | *self 39 | } 40 | } 41 | 42 | impl Copy for ChunkSize {} 43 | 44 | impl ChunkSize { 45 | pub const DEFAULT: Self = const_unwrap(ChunkSizeHint::DEFAULT.calc_size()); 46 | 47 | pub const fn from_hint(size_hint: usize) -> Option { 48 | ChunkSizeHint::new(size_hint).calc_size() 49 | } 50 | 51 | pub const fn from_capacity(layout: Layout) -> Option { 52 | attempt!(ChunkSizeHint::for_capacity(layout)).calc_size() 53 | } 54 | 55 | /// See [`chunk_size_config::ChunkSizeConfig::align_size`]. 56 | pub const fn align_allocation_size(self, size: usize) -> usize { 57 | _ = self; 58 | config::().align_size(size) 59 | } 60 | 61 | pub const fn layout(self) -> Option { 62 | let size = self.size.get(); 63 | let align = core::mem::align_of::>(); 64 | match Layout::from_size_align(size, align) { 65 | Ok(ok) => Some(ok), 66 | Err(_) => None, 67 | } 68 | } 69 | } 70 | pub struct ChunkSizeHint(usize, PhantomData<*const A>); 71 | 72 | impl Clone for ChunkSizeHint { 73 | fn clone(&self) -> Self { 74 | *self 75 | } 76 | } 77 | 78 | impl Copy for ChunkSizeHint {} 79 | 80 | impl ChunkSizeHint { 81 | pub const DEFAULT: Self = Self::new(512); 82 | 83 | pub const fn new(size_hint: usize) -> Self { 84 | Self(size_hint, PhantomData) 85 | } 86 | 87 | pub const fn for_capacity(layout: Layout) -> Option { 88 | Some(Self(attempt!(config::().calc_hint_from_capacity(layout)), PhantomData)) 89 | } 90 | 91 | pub const fn calc_size(self) -> Option> { 92 | Some(ChunkSize { 93 | size: attempt!(config::().calc_size_from_hint(self.0)), 94 | marker: PhantomData, 95 | }) 96 | } 97 | 98 | pub const fn max(self, other: Self) -> Self { 99 | if self.0 > other.0 { 100 | self 101 | } else { 102 | other 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/destructure.rs: -------------------------------------------------------------------------------- 1 | /// Allows you to destructure structs that have a drop implementation. 2 | /// 3 | /// The drop implementation will not be called for `$ty` nor for any field that is not bound. 4 | macro_rules! destructure { 5 | (let $ty:ty { 6 | $($field:ident $(: $field_alias:ident)?),* $(,)? 7 | } = $value:expr) => { 8 | let value: $ty = $value; 9 | let value = ::core::mem::ManuallyDrop::new(value); 10 | 11 | #[allow(dead_code)] 12 | const _: () = assert!(!$crate::destructure::has_duplicates(&[$(stringify!($field)),*]), "you can't destructure a field twice"); 13 | 14 | $( 15 | let $crate::destructure::or!($($field_alias)? $field) = unsafe { ::core::ptr::read(&value.$field) }; 16 | )* 17 | }; 18 | } 19 | 20 | pub(crate) use destructure; 21 | 22 | macro_rules! or { 23 | ($this:ident $that:ident) => { 24 | $this 25 | }; 26 | ($that:ident) => { 27 | $that 28 | }; 29 | } 30 | 31 | pub(crate) use or; 32 | 33 | pub(crate) const fn has_duplicates(strings: &[&str]) -> bool { 34 | let mut x = 0; 35 | 36 | while x < strings.len() { 37 | let mut y = x + 1; 38 | 39 | while y < strings.len() { 40 | if str_eq(strings[x], strings[y]) { 41 | return true; 42 | } 43 | 44 | y += 1; 45 | } 46 | 47 | x += 1; 48 | } 49 | 50 | false 51 | } 52 | 53 | const fn str_eq(a: &str, b: &str) -> bool { 54 | let a = a.as_bytes(); 55 | let b = b.as_bytes(); 56 | 57 | if a.len() != b.len() { 58 | return false; 59 | } 60 | 61 | let mut i = 0; 62 | 63 | while i < a.len() { 64 | if a[i] != b[i] { 65 | return false; 66 | } 67 | 68 | i += 1; 69 | } 70 | 71 | true 72 | } 73 | 74 | #[cfg(test)] 75 | mod tests { 76 | use std::string::String; 77 | 78 | use super::*; 79 | 80 | #[test] 81 | fn example() { 82 | pub struct Foo { 83 | bar: String, 84 | baz: String, 85 | } 86 | 87 | impl Drop for Foo { 88 | fn drop(&mut self) { 89 | unreachable!() 90 | } 91 | } 92 | 93 | let foo = Foo { 94 | bar: "bar".into(), 95 | baz: "baz".into(), 96 | }; 97 | 98 | // won't compile 99 | // let Foo { bar: qux, baz } = foo; 100 | 101 | destructure!(let Foo { bar: qux, baz } = foo); 102 | 103 | assert_eq!(qux, "bar"); 104 | assert_eq!(baz, "baz"); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/features/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "serde")] 2 | mod serde; 3 | 4 | #[cfg(feature = "nightly-allocator-api")] 5 | pub mod nightly_allocator_api; 6 | 7 | #[cfg(feature = "allocator-api2-02")] 8 | pub mod allocator_api2_02; 9 | 10 | #[cfg(feature = "allocator-api2-03")] 11 | pub mod allocator_api2_03; 12 | 13 | #[cfg(feature = "bytemuck")] 14 | pub mod bytemuck; 15 | 16 | #[cfg(feature = "zerocopy-08")] 17 | pub mod zerocopy_08; 18 | -------------------------------------------------------------------------------- /src/from_utf16_error.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | 3 | /// A possible error value when converting a string from a UTF-16 byte slice. 4 | /// 5 | /// This type is the error type for the `from_utf16_in` method on 6 | /// ([Mut])[BumpString]. 7 | /// 8 | /// # Examples 9 | /// 10 | /// ``` 11 | /// # use bump_scope::{Bump, BumpString}; 12 | /// # let bump: Bump = Bump::new(); 13 | /// 14 | /// // 𝄞muic 15 | /// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075, 16 | /// 0xD800, 0x0069, 0x0063]; 17 | /// 18 | /// assert!(BumpString::from_utf16_in(v, &bump).is_err()); 19 | /// ``` 20 | /// 21 | /// [Mut]: crate::MutBumpString::from_utf16_in 22 | /// [BumpString]: crate::BumpString::from_utf16_in 23 | #[derive(Debug)] 24 | pub struct FromUtf16Error(pub(crate) ()); 25 | 26 | impl fmt::Display for FromUtf16Error { 27 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 28 | fmt::Display::fmt("invalid utf-16: lone surrogate found", f) 29 | } 30 | } 31 | 32 | #[cfg(not(feature = "std"))] 33 | #[rustversion::since(1.81)] 34 | impl core::error::Error for FromUtf16Error {} 35 | 36 | #[cfg(feature = "std")] 37 | impl std::error::Error for FromUtf16Error {} 38 | -------------------------------------------------------------------------------- /src/from_utf8_error.rs: -------------------------------------------------------------------------------- 1 | use core::{fmt, ops::Deref, str::Utf8Error}; 2 | 3 | /// A possible error value when converting a string from a UTF-8 byte vector. 4 | /// 5 | /// This type is the error type for the [`BumpString::from_utf8`](crate::BumpString::from_utf8), [`MutBumpString::from_utf8`](crate::MutBumpString::from_utf8) and [`BumpBox::from_utf8`](crate::BumpBox::from_utf8). It 6 | /// is designed in such a way to carefully avoid reallocations: the 7 | /// [`into_bytes`](FromUtf8Error::into_bytes) method will give back the byte vector that was used in the 8 | /// conversion attempt. 9 | /// 10 | /// The [`Utf8Error`] type provided by [`std::str`] represents an error that may 11 | /// occur when converting a slice of [`u8`]s to a [`&str`]. In this sense, it's 12 | /// an analogue to `FromUtf8Error`, and you can get one from a `FromUtf8Error` 13 | /// through the [`utf8_error`] method. 14 | /// 15 | /// [`Utf8Error`]: core::str::Utf8Error 16 | /// [`std::str`]: core::str "std::str" 17 | /// [`&str`]: prim@str "&str" 18 | /// [`utf8_error`]: FromUtf8Error::utf8_error 19 | #[derive(Debug, PartialEq, Eq)] 20 | #[cfg_attr(feature = "panic-on-alloc", derive(Clone))] 21 | pub struct FromUtf8Error { 22 | bytes: Bytes, 23 | error: Utf8Error, 24 | } 25 | 26 | impl FromUtf8Error { 27 | #[inline(always)] 28 | pub(crate) const fn new(error: Utf8Error, bytes: Bytes) -> Self { 29 | Self { bytes, error } 30 | } 31 | 32 | /// Returns a slice of [`u8`]s bytes that were attempted to convert to a `String`. 33 | #[must_use] 34 | #[inline(always)] 35 | pub fn as_bytes(&self) -> &[u8] 36 | where 37 | Bytes: Deref, 38 | { 39 | &self.bytes 40 | } 41 | 42 | /// Returns the bytes that were attempted to convert to a `String`. 43 | #[must_use = "`self` will be dropped if the result is not used"] 44 | #[inline(always)] 45 | pub fn into_bytes(self) -> Bytes { 46 | self.bytes 47 | } 48 | 49 | /// Fetch a `Utf8Error` to get more details about the conversion failure. 50 | /// 51 | /// The [`Utf8Error`] type provided by [`std::str`] represents an error that may 52 | /// occur when converting a slice of [`u8`]s to a [`&str`]. In this sense, it's 53 | /// an analogue to `FromUtf8Error`. See its documentation for more details 54 | /// on using it. 55 | /// 56 | /// [`std::str`]: core::str "std::str" 57 | /// [`&str`]: prim@str "&str" 58 | #[inline(always)] 59 | pub fn utf8_error(&self) -> Utf8Error { 60 | self.error 61 | } 62 | } 63 | 64 | impl fmt::Display for FromUtf8Error { 65 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 66 | fmt::Display::fmt(&self.error, f) 67 | } 68 | } 69 | 70 | #[cfg(not(feature = "std"))] 71 | #[rustversion::since(1.81)] 72 | impl core::error::Error for FromUtf8Error {} 73 | 74 | #[cfg(feature = "std")] 75 | impl std::error::Error for FromUtf8Error {} 76 | -------------------------------------------------------------------------------- /src/layout.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | alloc::{Layout, LayoutError}, 3 | fmt, 4 | ops::Deref, 5 | }; 6 | 7 | pub(crate) trait LayoutProps: Deref + Copy { 8 | const ALIGN_IS_CONST: bool; 9 | const SIZE_IS_CONST: bool; 10 | const SIZE_IS_MULTIPLE_OF_ALIGN: bool; 11 | } 12 | 13 | #[derive(Clone, Copy)] 14 | pub(crate) struct SizedLayout(Layout); 15 | 16 | impl LayoutProps for SizedLayout { 17 | const ALIGN_IS_CONST: bool = true; 18 | const SIZE_IS_CONST: bool = true; 19 | const SIZE_IS_MULTIPLE_OF_ALIGN: bool = true; 20 | } 21 | 22 | impl SizedLayout { 23 | #[inline(always)] 24 | pub(crate) const fn new() -> Self { 25 | Self(Layout::new::()) 26 | } 27 | } 28 | 29 | impl Deref for SizedLayout { 30 | type Target = Layout; 31 | 32 | fn deref(&self) -> &Self::Target { 33 | &self.0 34 | } 35 | } 36 | 37 | /// This must be `pub` because we use it in `supported_minimum_alignment::Sealed` which is pub. 38 | /// The current msrv denies us using `pub(crate)` with the error: 39 | /// ```txt 40 | /// error[E0446]: crate-private type `ArrayLayout` in public interface 41 | /// ``` 42 | #[derive(Clone, Copy)] 43 | pub struct ArrayLayout(Layout); 44 | 45 | impl LayoutProps for ArrayLayout { 46 | const ALIGN_IS_CONST: bool = true; 47 | const SIZE_IS_CONST: bool = false; 48 | const SIZE_IS_MULTIPLE_OF_ALIGN: bool = true; 49 | } 50 | 51 | impl ArrayLayout { 52 | #[inline(always)] 53 | pub(crate) fn for_value(value: &[T]) -> Self { 54 | Self(Layout::for_value(value)) 55 | } 56 | 57 | #[inline(always)] 58 | pub(crate) fn array(len: usize) -> Result { 59 | Ok(Self(Layout::array::(len)?)) 60 | } 61 | 62 | #[inline(always)] 63 | pub(crate) const fn from_layout(layout: Layout) -> Result { 64 | if layout.size() % layout.align() == 0 { 65 | Ok(ArrayLayout(layout)) 66 | } else { 67 | Err(ArrayLayoutError) 68 | } 69 | } 70 | 71 | #[inline(always)] 72 | pub(crate) const fn from_size_align(size: usize, align: usize) -> Result { 73 | match Layout::from_size_align(size, align) { 74 | Ok(layout) => Self::from_layout(layout), 75 | Err(_) => Err(ArrayLayoutError), 76 | } 77 | } 78 | } 79 | 80 | impl Deref for ArrayLayout { 81 | type Target = Layout; 82 | 83 | fn deref(&self) -> &Self::Target { 84 | &self.0 85 | } 86 | } 87 | 88 | #[derive(Clone, Copy)] 89 | pub(crate) struct CustomLayout(pub(crate) Layout); 90 | 91 | impl LayoutProps for CustomLayout { 92 | const ALIGN_IS_CONST: bool = false; 93 | const SIZE_IS_CONST: bool = false; 94 | const SIZE_IS_MULTIPLE_OF_ALIGN: bool = false; 95 | } 96 | 97 | impl Deref for CustomLayout { 98 | type Target = Layout; 99 | 100 | fn deref(&self) -> &Self::Target { 101 | &self.0 102 | } 103 | } 104 | 105 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] 106 | pub(crate) struct ArrayLayoutError; 107 | 108 | impl fmt::Display for ArrayLayoutError { 109 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 110 | f.write_str("invalid parameters to ArrayLayout constructor") 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/mut_bump_allocator_scope.rs: -------------------------------------------------------------------------------- 1 | use crate::{BumpAllocatorScope, MutBumpAllocator}; 2 | 3 | /// Shorthand for [MutBumpAllocator] + [BumpAllocatorScope]<'a> 4 | pub trait MutBumpAllocatorScope<'a>: MutBumpAllocator + BumpAllocatorScope<'a> {} 5 | impl<'a, T: MutBumpAllocator + BumpAllocatorScope<'a>> MutBumpAllocatorScope<'a> for T {} 6 | -------------------------------------------------------------------------------- /src/no_drop.rs: -------------------------------------------------------------------------------- 1 | /// This trait marks types that don't need dropping. 2 | /// 3 | /// This trait is a best effort for modeling such a constraint. It is not implemented for all types that don't need dropping. 4 | /// 5 | /// Every `T where T: Copy` and every `[T] where T: NoDrop` automatically implements `NoDrop`. 6 | /// 7 | /// It is used as a bound for [`BumpBox`]'s [`into_ref`] and [`into_mut`] so you don't accidentally omit a drop that does matter. 8 | /// 9 | /// [`BumpBox`]: crate::BumpBox 10 | /// [`into_ref`]: crate::BumpBox::into_ref 11 | /// [`into_mut`]: crate::BumpBox::into_mut 12 | pub trait NoDrop {} 13 | 14 | impl NoDrop for str {} 15 | impl NoDrop for T {} 16 | impl NoDrop for [T] {} 17 | 18 | impl NoDrop for core::ffi::CStr {} 19 | 20 | #[cfg(feature = "std")] 21 | mod std_impl { 22 | use super::NoDrop; 23 | 24 | impl NoDrop for std::ffi::OsStr {} 25 | impl NoDrop for std::path::Path {} 26 | } 27 | -------------------------------------------------------------------------------- /src/owned_slice/extract_if.rs: -------------------------------------------------------------------------------- 1 | use core::ptr::NonNull; 2 | 3 | use crate::{polyfill::non_null, BumpBox}; 4 | 5 | /// An iterator which uses a closure to determine if an element should be removed. 6 | /// 7 | /// This struct is created by the `extract_if` method on 8 | /// [`BumpBox`](BumpBox::extract_if), 9 | /// [`FixedBumpVec`](crate::FixedBumpVec::extract_if), 10 | /// [`BumpVec`](crate::BumpVec::extract_if) and 11 | /// [`MutBumpVec`](crate::MutBumpVec::extract_if). 12 | /// 13 | /// See their documentation for more. 14 | /// 15 | /// # Example 16 | /// 17 | /// ``` 18 | /// use bump_scope::{Bump, owned_slice::ExtractIf}; 19 | /// let bump: Bump = Bump::new(); 20 | /// 21 | /// let mut v = bump.alloc_slice_copy(&[0, 1, 2]); 22 | /// let iter: ExtractIf<'_, _, _> = v.extract_if(|x| *x % 2 == 0); 23 | /// ``` 24 | #[derive(Debug)] 25 | #[must_use = "iterators are lazy and do nothing unless consumed"] 26 | pub struct ExtractIf<'a, T, F> 27 | where 28 | F: FnMut(&mut T) -> bool, 29 | { 30 | ptr: &'a mut NonNull<[T]>, 31 | index: usize, 32 | drained_count: usize, 33 | original_len: usize, 34 | filter: F, 35 | } 36 | 37 | impl<'a, T, F> ExtractIf<'a, T, F> 38 | where 39 | F: FnMut(&mut T) -> bool, 40 | { 41 | pub(crate) fn new<'a2>(boxed: &'a mut BumpBox<'a2, [T]>, filter: F) -> Self { 42 | // When the ExtractIf is first created, it shortens the length of 43 | // the source boxed slice to make sure no uninitialized or moved-from elements 44 | // are accessible at all if the ExtractIf's destructor never gets to run. 45 | // 46 | // The 'a2 lifetime is shortened to 'a even though &'b mut BumpBox<'a2> is invariant 47 | // over 'a2. We are careful not to expose any api where that could cause issues. 48 | 49 | let ptr = unsafe { boxed.mut_ptr() }; 50 | let len = ptr.len(); 51 | 52 | non_null::set_len(ptr, 0); 53 | 54 | Self { 55 | ptr, 56 | index: 0, 57 | drained_count: 0, 58 | original_len: len, 59 | filter, 60 | } 61 | } 62 | } 63 | 64 | impl Iterator for ExtractIf<'_, T, F> 65 | where 66 | F: FnMut(&mut T) -> bool, 67 | { 68 | type Item = T; 69 | 70 | fn next(&mut self) -> Option { 71 | unsafe { 72 | while self.index < self.original_len { 73 | let start_ptr = non_null::as_non_null_ptr(*self.ptr); 74 | let mut value_ptr = non_null::add(start_ptr, self.index); 75 | 76 | let drained = (self.filter)(value_ptr.as_mut()); 77 | 78 | // Update the index *after* the predicate is called. If the index 79 | // is updated prior and the predicate panics, the element at this 80 | // index would be leaked. 81 | self.index += 1; 82 | 83 | if drained { 84 | self.drained_count += 1; 85 | return Some(value_ptr.as_ptr().read()); 86 | } else if self.drained_count > 0 { 87 | let src = value_ptr; 88 | let dst = non_null::sub(value_ptr, self.drained_count); 89 | non_null::copy_nonoverlapping(src, dst, 1); 90 | } 91 | } 92 | None 93 | } 94 | } 95 | 96 | #[inline] 97 | fn size_hint(&self) -> (usize, Option) { 98 | (0, Some(self.original_len - self.index)) 99 | } 100 | } 101 | 102 | impl Drop for ExtractIf<'_, T, F> 103 | where 104 | F: FnMut(&mut T) -> bool, 105 | { 106 | fn drop(&mut self) { 107 | unsafe { 108 | if self.index < self.original_len && self.drained_count > 0 { 109 | // This is a pretty messed up state, and there isn't really an 110 | // obviously right thing to do. We don't want to keep trying 111 | // to execute `pred`, so we just backshift all the unprocessed 112 | // elements and tell the vec that they still exist. The backshift 113 | // is required to prevent a double-drop of the last successfully 114 | // drained item prior to a panic in the predicate. 115 | let ptr = non_null::as_non_null_ptr(*self.ptr); 116 | let src = non_null::add(ptr, self.index); 117 | let dst = non_null::sub(src, self.drained_count); 118 | let tail_len = self.original_len - self.index; 119 | non_null::copy(src, dst, tail_len); 120 | } 121 | 122 | non_null::set_len(self.ptr, self.original_len - self.drained_count); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/owned_str.rs: -------------------------------------------------------------------------------- 1 | mod drain; 2 | 3 | pub use drain::Drain; 4 | -------------------------------------------------------------------------------- /src/owned_str/drain.rs: -------------------------------------------------------------------------------- 1 | use core::{fmt, iter::FusedIterator, ptr::NonNull, str::Chars}; 2 | 3 | use crate::BumpBox; 4 | 5 | /// A draining iterator for an owned string. 6 | /// 7 | /// This struct is created by the `drain` method on 8 | /// [`BumpBox`](BumpBox::drain), 9 | /// [`FixedBumpString`](crate::FixedBumpString::drain), 10 | /// [`BumpString`](crate::BumpString::drain) and 11 | /// [`MutBumpString`](crate::MutBumpString::drain). 12 | /// 13 | /// See their documentation for more. 14 | pub struct Drain<'a> { 15 | /// Will be used as `&'a mut BumpBox` in the destructor 16 | pub(crate) string: NonNull>, 17 | /// Start of part to remove 18 | pub(crate) start: usize, 19 | /// End of part to remove 20 | pub(crate) end: usize, 21 | /// Current remaining range to remove 22 | pub(crate) iter: Chars<'a>, 23 | } 24 | 25 | impl fmt::Debug for Drain<'_> { 26 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 27 | f.debug_tuple("Drain").field(&self.as_str()).finish() 28 | } 29 | } 30 | 31 | unsafe impl Sync for Drain<'_> {} 32 | unsafe impl Send for Drain<'_> {} 33 | 34 | impl Drop for Drain<'_> { 35 | fn drop(&mut self) { 36 | unsafe { 37 | // Use `BumpBox::<[u8]>::drain`. "Reaffirm" the bounds checks to avoid 38 | // panic code being inserted again. 39 | let self_vec = self.string.as_mut().as_mut_bytes(); 40 | 41 | if self.start <= self.end && self.end <= self_vec.len() { 42 | self_vec.drain(self.start..self.end); 43 | } 44 | } 45 | } 46 | } 47 | 48 | impl Drain<'_> { 49 | /// Returns the remaining (sub)string of this iterator as a slice. 50 | /// 51 | /// # Examples 52 | /// 53 | /// ``` 54 | /// # use bump_scope::Bump; 55 | /// # let bump: Bump = Bump::new(); /// 56 | /// let mut s = bump.alloc_str("abc"); 57 | /// let mut drain = s.drain(..); 58 | /// assert_eq!(drain.as_str(), "abc"); 59 | /// let _ = drain.next().unwrap(); 60 | /// assert_eq!(drain.as_str(), "bc"); 61 | /// ``` 62 | #[must_use] 63 | pub fn as_str(&self) -> &str { 64 | self.iter.as_str() 65 | } 66 | } 67 | 68 | impl AsRef for Drain<'_> { 69 | fn as_ref(&self) -> &str { 70 | self.as_str() 71 | } 72 | } 73 | 74 | impl AsRef<[u8]> for Drain<'_> { 75 | fn as_ref(&self) -> &[u8] { 76 | self.as_str().as_bytes() 77 | } 78 | } 79 | 80 | impl Iterator for Drain<'_> { 81 | type Item = char; 82 | 83 | #[inline] 84 | fn next(&mut self) -> Option { 85 | self.iter.next() 86 | } 87 | 88 | #[inline] 89 | fn size_hint(&self) -> (usize, Option) { 90 | self.iter.size_hint() 91 | } 92 | 93 | #[inline] 94 | fn last(mut self) -> Option { 95 | self.next_back() 96 | } 97 | } 98 | 99 | impl DoubleEndedIterator for Drain<'_> { 100 | #[inline] 101 | fn next_back(&mut self) -> Option { 102 | self.iter.next_back() 103 | } 104 | } 105 | 106 | impl FusedIterator for Drain<'_> {} 107 | -------------------------------------------------------------------------------- /src/partial_eq.rs: -------------------------------------------------------------------------------- 1 | use crate::{BumpAllocator, BumpString, BumpVec, FixedBumpVec, MutBumpAllocator, MutBumpString, MutBumpVec, MutBumpVecRev}; 2 | 3 | macro_rules! impl_slice_eq { 4 | ([$($vars:tt)*] $lhs:ty, $rhs:ty $(where $ty:ty: $bound:ident)?) => { 5 | impl PartialEq<$rhs> for $lhs 6 | where 7 | T: PartialEq, 8 | $($ty: $bound)? 9 | { 10 | #[inline] 11 | fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } 12 | #[inline] 13 | fn ne(&self, other: &$rhs) -> bool { self[..] != other[..] } 14 | } 15 | } 16 | } 17 | 18 | impl_slice_eq! { [] FixedBumpVec<'_, T>, FixedBumpVec<'_, U> } 19 | impl_slice_eq! { [] FixedBumpVec<'_, T>, [U] } 20 | impl_slice_eq! { [] FixedBumpVec<'_, T>, &[U] } 21 | impl_slice_eq! { [] FixedBumpVec<'_, T>, &mut [U] } 22 | impl_slice_eq! { [] [T], FixedBumpVec<'_, U> } 23 | impl_slice_eq! { [] &[T], FixedBumpVec<'_, U> } 24 | impl_slice_eq! { [] &mut [T], FixedBumpVec<'_, U> } 25 | impl_slice_eq! { [const N: usize] FixedBumpVec<'_, T>, [U; N] } 26 | impl_slice_eq! { [const N: usize] FixedBumpVec<'_, T>, &[U; N] } 27 | impl_slice_eq! { [const N: usize] FixedBumpVec<'_, T>, &mut [U; N] } 28 | 29 | impl_slice_eq! { [A1: BumpAllocator, A2: BumpAllocator] BumpVec, BumpVec } 30 | impl_slice_eq! { [A: BumpAllocator] BumpVec, [U] } 31 | impl_slice_eq! { [A: BumpAllocator] BumpVec, &[U] } 32 | impl_slice_eq! { [A: BumpAllocator] BumpVec, &mut [U] } 33 | impl_slice_eq! { [A: BumpAllocator] [T], BumpVec } 34 | impl_slice_eq! { [A: BumpAllocator] &[T], BumpVec } 35 | impl_slice_eq! { [A: BumpAllocator] &mut [T], BumpVec } 36 | impl_slice_eq! { [A: BumpAllocator, const N: usize] BumpVec, [U; N] } 37 | impl_slice_eq! { [A: BumpAllocator, const N: usize] BumpVec, &[U; N] } 38 | impl_slice_eq! { [A: BumpAllocator, const N: usize] BumpVec, &mut [U; N] } 39 | 40 | impl_slice_eq! { [A1: MutBumpAllocator, A2: MutBumpAllocator] MutBumpVec, MutBumpVec } 41 | impl_slice_eq! { [A: MutBumpAllocator] MutBumpVec, [U] } 42 | impl_slice_eq! { [A: MutBumpAllocator] MutBumpVec, &[U] } 43 | impl_slice_eq! { [A: MutBumpAllocator] MutBumpVec, &mut [U] } 44 | impl_slice_eq! { [A: MutBumpAllocator] [T], MutBumpVec } 45 | impl_slice_eq! { [A: MutBumpAllocator] &[T], MutBumpVec } 46 | impl_slice_eq! { [A: MutBumpAllocator] &mut [T], MutBumpVec } 47 | impl_slice_eq! { [A: MutBumpAllocator, const N: usize] MutBumpVec, [U; N] } 48 | impl_slice_eq! { [A: MutBumpAllocator, const N: usize] MutBumpVec, &[U; N] } 49 | impl_slice_eq! { [A: MutBumpAllocator, const N: usize] MutBumpVec, &mut [U; N] } 50 | 51 | impl_slice_eq! { [A1: MutBumpAllocator, A2: MutBumpAllocator] MutBumpVecRev, MutBumpVecRev } 52 | impl_slice_eq! { [A: MutBumpAllocator] MutBumpVecRev, [U] } 53 | impl_slice_eq! { [A: MutBumpAllocator] MutBumpVecRev, &[U] } 54 | impl_slice_eq! { [A: MutBumpAllocator] MutBumpVecRev, &mut [U] } 55 | impl_slice_eq! { [A: MutBumpAllocator] [T], MutBumpVecRev } 56 | impl_slice_eq! { [A: MutBumpAllocator] &[T], MutBumpVecRev } 57 | impl_slice_eq! { [A: MutBumpAllocator] &mut [T], MutBumpVecRev } 58 | impl_slice_eq! { [A: MutBumpAllocator, const N: usize] MutBumpVecRev, [U; N] } 59 | impl_slice_eq! { [A: MutBumpAllocator, const N: usize] MutBumpVecRev, &[U; N] } 60 | impl_slice_eq! { [A: MutBumpAllocator, const N: usize] MutBumpVecRev, &mut [U; N] } 61 | 62 | macro_rules! impl_str_eq { 63 | ([$($vars:tt)*] $lhs:ty, $rhs:ty $(where $ty:ty: $bound:ident)?) => { 64 | impl<$($vars)*> PartialEq<$rhs> for $lhs 65 | where 66 | $($ty: $bound)? 67 | { 68 | #[inline] 69 | fn eq(&self, other: &$rhs) -> bool { &self[..] == &other[..] } 70 | #[inline] 71 | fn ne(&self, other: &$rhs) -> bool { &self[..] != &other[..] } 72 | } 73 | } 74 | } 75 | 76 | impl_str_eq! { [A1: BumpAllocator, A2: BumpAllocator] BumpString, BumpString } 77 | impl_str_eq! { [A: BumpAllocator] BumpString, str } 78 | impl_str_eq! { [A: BumpAllocator] BumpString, &str } 79 | impl_str_eq! { [A: BumpAllocator] BumpString, &mut str } 80 | impl_str_eq! { [A: BumpAllocator] str, BumpString } 81 | impl_str_eq! { [A: BumpAllocator] &str, BumpString } 82 | impl_str_eq! { [A: BumpAllocator] &mut str, BumpString } 83 | 84 | impl_str_eq! { [A1, A2] MutBumpString, MutBumpString } 85 | impl_str_eq! { [A] MutBumpString, str } 86 | impl_str_eq! { [A] MutBumpString, &str } 87 | impl_str_eq! { [A] MutBumpString, &mut str } 88 | impl_str_eq! { [A] str, MutBumpString } 89 | impl_str_eq! { [A] &str, MutBumpString } 90 | impl_str_eq! { [A] &mut str, MutBumpString } 91 | 92 | #[cfg(feature = "alloc")] 93 | mod alloc_impl { 94 | #[allow(clippy::wildcard_imports)] 95 | use super::*; 96 | 97 | use alloc_crate::{borrow::Cow, string::String}; 98 | 99 | impl_str_eq! { [A: BumpAllocator] BumpString, String } 100 | impl_str_eq! { [A: BumpAllocator] String, BumpString } 101 | impl_str_eq! { [A: BumpAllocator] BumpString, Cow<'_, str> } 102 | impl_str_eq! { [A: BumpAllocator] Cow<'_, str>, BumpString } 103 | 104 | impl_str_eq! { [A] MutBumpString, String } 105 | impl_str_eq! { [A] String, MutBumpString } 106 | impl_str_eq! { [A] MutBumpString, Cow<'_, str> } 107 | impl_str_eq! { [A] Cow<'_, str>, MutBumpString } 108 | } 109 | -------------------------------------------------------------------------------- /src/polyfill/hint.rs: -------------------------------------------------------------------------------- 1 | /// See [`std::hint::assert_unchecked`]. 2 | pub(crate) unsafe fn assert_unchecked(condition: bool) { 3 | if !condition { 4 | unsafe { core::hint::unreachable_unchecked() } 5 | } 6 | } 7 | 8 | /// Not part of std. 9 | #[cold] 10 | #[inline(always)] 11 | #[allow(dead_code)] 12 | pub(crate) fn cold() {} 13 | 14 | /// Not part of std. 15 | #[inline(always)] 16 | #[allow(dead_code)] 17 | pub(crate) fn likely(condition: bool) -> bool { 18 | if condition { 19 | // ... 20 | } else { 21 | cold(); 22 | } 23 | 24 | condition 25 | } 26 | 27 | /// Not part of std. 28 | #[inline(always)] 29 | #[allow(dead_code)] 30 | pub(crate) fn unlikely(condition: bool) -> bool { 31 | if condition { 32 | cold(); 33 | } else { 34 | // ... 35 | } 36 | 37 | condition 38 | } 39 | -------------------------------------------------------------------------------- /src/polyfill/iter.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::pedantic)] 2 | #![allow(clippy::toplevel_ref_arg)] 3 | 4 | /// See [`std::iter::Iterator::partition_in_place`]. 5 | pub(crate) fn partition_in_place<'a, T: 'a, P>( 6 | mut iter: impl DoubleEndedIterator, 7 | ref mut predicate: P, 8 | ) -> usize 9 | where 10 | P: FnMut(&T) -> bool, 11 | { 12 | // STD-FIXME: should we worry about the count overflowing? The only way to have more than 13 | // `usize::MAX` mutable references is with ZSTs, which aren't useful to partition... 14 | 15 | // These closure "factory" functions exist to avoid genericity in `Self`. 16 | 17 | #[inline] 18 | fn is_false<'a, T>( 19 | predicate: &'a mut impl FnMut(&T) -> bool, 20 | true_count: &'a mut usize, 21 | ) -> impl FnMut(&&mut T) -> bool + 'a { 22 | move |x| { 23 | let p = predicate(&**x); 24 | *true_count += p as usize; 25 | !p 26 | } 27 | } 28 | 29 | #[inline] 30 | fn is_true(predicate: &mut impl FnMut(&T) -> bool) -> impl FnMut(&&mut T) -> bool + '_ { 31 | move |x| predicate(&**x) 32 | } 33 | 34 | // Repeatedly find the first `false` and swap it with the last `true`. 35 | let mut true_count = 0; 36 | while let Some(head) = iter.find(is_false(predicate, &mut true_count)) { 37 | if let Some(tail) = iter.rfind(is_true(predicate)) { 38 | crate::mem::swap(head, tail); 39 | true_count += 1; 40 | } else { 41 | break; 42 | } 43 | } 44 | true_count 45 | } 46 | -------------------------------------------------------------------------------- /src/polyfill/layout.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "alloc")] 2 | use core::{alloc::Layout, num::NonZeroUsize, ptr::NonNull}; 3 | 4 | /// See [`std::alloc::Layout::dangling`]. 5 | #[must_use] 6 | #[inline] 7 | #[cfg(feature = "alloc")] 8 | pub(crate) const fn dangling(layout: Layout) -> NonNull { 9 | unsafe { super::non_null::without_provenance(NonZeroUsize::new_unchecked(layout.align())) } 10 | } 11 | -------------------------------------------------------------------------------- /src/polyfill/mod.rs: -------------------------------------------------------------------------------- 1 | //! Stuff that is missing from the msrv's std. 2 | //! 3 | //! This module also includes utility functions 4 | //! that are not from the standard library. 5 | #![allow( 6 | // it's not the same in terms of strict provenance 7 | clippy::transmutes_expressible_as_ptr_casts, 8 | // it's not the same in terms of strict provenance 9 | clippy::useless_transmute, 10 | )] 11 | 12 | pub(crate) mod hint; 13 | pub(crate) mod iter; 14 | pub(crate) mod layout; 15 | pub(crate) mod non_null; 16 | pub(crate) mod option; 17 | pub(crate) mod pointer; 18 | pub(crate) mod pointer_mut; 19 | pub(crate) mod ptr; 20 | pub(crate) mod slice; 21 | pub(crate) mod str; 22 | pub(crate) mod usize; 23 | 24 | use core::mem::{size_of, ManuallyDrop}; 25 | 26 | /// Not part of std. 27 | /// 28 | /// A version of [`std::mem::transmute`] that can transmute between generic types. 29 | pub(crate) unsafe fn transmute_value(a: A) -> B { 30 | assert!(size_of::() == size_of::()); 31 | core::mem::transmute_copy(&ManuallyDrop::new(a)) 32 | } 33 | 34 | /// Not part of std. 35 | /// 36 | /// A safer [`std::mem::transmute`]. 37 | pub(crate) const unsafe fn transmute_ref(a: &A) -> &B { 38 | assert!(size_of::() == size_of::()); 39 | &*(a as *const A).cast::() 40 | } 41 | 42 | /// Not part of std. 43 | /// 44 | /// A safer [`std::mem::transmute`]. 45 | pub(crate) unsafe fn transmute_mut(a: &mut A) -> &mut B { 46 | assert!(size_of::() == size_of::()); 47 | &mut *(a as *mut A).cast::() 48 | } 49 | -------------------------------------------------------------------------------- /src/polyfill/option.rs: -------------------------------------------------------------------------------- 1 | /// Not part of std. 2 | #[inline(always)] 3 | pub(crate) const fn const_unwrap(option: Option) -> T { 4 | match option { 5 | Some(value) => value, 6 | None => unwrap_failed(), 7 | } 8 | } 9 | 10 | #[cold] 11 | #[inline(never)] 12 | #[track_caller] 13 | const fn unwrap_failed() -> ! { 14 | panic!("called `Option::unwrap()` on a `None` value") 15 | } 16 | -------------------------------------------------------------------------------- /src/polyfill/pointer.rs: -------------------------------------------------------------------------------- 1 | use core::{mem, ptr}; 2 | 3 | use crate::polyfill; 4 | 5 | /// See `<*const T>::len`. 6 | /// 7 | /// This implementation has an additional safety invariant though. 8 | /// 9 | /// # Safety 10 | /// `ptr` must be valid to be turned into a reference. 11 | #[must_use] 12 | #[inline(always)] 13 | pub(crate) unsafe fn len(ptr: *const [T]) -> usize { 14 | // if we followed clippy's advice, check would instead complain about `dangerous_implicit_autorefs` 15 | #[allow(clippy::needless_borrow)] 16 | (&(*ptr)).len() 17 | } 18 | 19 | /// See `<*const T>::offset_from_unsigned`. 20 | #[inline] 21 | #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces 22 | #[allow(clippy::cast_sign_loss)] 23 | #[allow(clippy::checked_conversions)] 24 | pub(crate) unsafe fn offset_from_unsigned(this: *const T, origin: *const T) -> usize { 25 | polyfill::hint::assert_unchecked(this >= origin); 26 | let pointee_size = mem::size_of::(); 27 | assert!(0 < pointee_size && pointee_size <= isize::MAX as usize); 28 | this.offset_from(origin) as usize 29 | } 30 | 31 | /// Not part of std. 32 | /// 33 | /// Putting the expression in a function helps llvm to realize that it can initialize the value 34 | /// at this pointer instead of allocating it on the stack and then copying it over. 35 | #[inline(always)] 36 | pub(crate) unsafe fn write_with(ptr: *mut T, f: impl FnOnce() -> T) { 37 | ptr::write(ptr, f()); 38 | } 39 | 40 | /// See `<*const T>::cast_mut`. 41 | #[inline(always)] 42 | pub(crate) const fn cast_mut(ptr: *const T) -> *mut T { 43 | ptr as _ 44 | } 45 | 46 | /// See `<*const T>::addr`. 47 | #[must_use] 48 | #[inline(always)] 49 | pub(crate) fn addr(ptr: *const T) -> usize { 50 | // A pointer-to-integer transmute currently has exactly the right semantics: it returns the 51 | // address without exposing the provenance. Note that this is *not* a stable guarantee about 52 | // transmute semantics, it relies on sysroot crates having special status. 53 | // SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the 54 | // provenance). 55 | unsafe { mem::transmute(ptr.cast::<()>()) } 56 | } 57 | -------------------------------------------------------------------------------- /src/polyfill/pointer_mut.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | 3 | /// See `<*mut T>::addr`. 4 | #[must_use] 5 | #[inline(always)] 6 | pub(crate) fn addr(ptr: *mut T) -> usize { 7 | // A pointer-to-integer transmute currently has exactly the right semantics: it returns the 8 | // address without exposing the provenance. Note that this is *not* a stable guarantee about 9 | // transmute semantics, it relies on sysroot crates having special status. 10 | // SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the 11 | // provenance). 12 | unsafe { mem::transmute(ptr.cast::<()>()) } 13 | } 14 | 15 | /// See `<*mut T>::with_addr`. 16 | #[inline] 17 | #[must_use] 18 | #[allow(clippy::cast_possible_wrap)] 19 | pub(crate) fn with_addr(ptr: *mut T, addr: usize) -> *mut T { 20 | // This should probably be an intrinsic to avoid doing any sort of arithmetic, but 21 | // meanwhile, we can implement it with `wrapping_offset`, which preserves the pointer's 22 | // provenance. 23 | let self_addr = self::addr(ptr) as isize; 24 | let dest_addr = addr as isize; 25 | let offset = dest_addr.wrapping_sub(self_addr); 26 | wrapping_byte_offset(ptr, offset) 27 | } 28 | 29 | /// See `<*mut T>::wrapping_byte_offset`. 30 | #[must_use] 31 | #[inline(always)] 32 | pub(crate) const fn wrapping_byte_offset(ptr: *mut T, count: isize) -> *mut T { 33 | ptr.cast::().wrapping_offset(count).cast() 34 | } 35 | -------------------------------------------------------------------------------- /src/polyfill/ptr.rs: -------------------------------------------------------------------------------- 1 | /// See [`std::ptr::from_ref`]. 2 | #[must_use] 3 | #[inline(always)] 4 | pub(crate) fn from_ref(r: &T) -> *const T { 5 | r 6 | } 7 | 8 | /// See [`std::ptr::from_mut`]. 9 | #[must_use] 10 | #[inline(always)] 11 | pub(crate) fn from_mut(r: &mut T) -> *mut T { 12 | r 13 | } 14 | 15 | /// See [`std::ptr::without_provenance_mut`]. 16 | #[must_use] 17 | #[inline(always)] 18 | #[cfg(feature = "alloc")] 19 | pub(crate) const fn without_provenance_mut(addr: usize) -> *mut T { 20 | // An int-to-pointer transmute currently has exactly the intended semantics: it creates a 21 | // pointer without provenance. Note that this is *not* a stable guarantee about transmute 22 | // semantics, it relies on sysroot crates having special status. 23 | // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that 24 | // pointer). 25 | unsafe { core::mem::transmute(addr) } 26 | } 27 | -------------------------------------------------------------------------------- /src/polyfill/slice.rs: -------------------------------------------------------------------------------- 1 | use core::ops; 2 | 3 | pub(crate) use core::slice::*; 4 | 5 | use crate::polyfill::usize::unchecked_sub; 6 | 7 | #[cold] 8 | #[inline(never)] 9 | #[track_caller] 10 | pub(crate) const fn slice_start_index_overflow_fail() -> ! { 11 | panic!("attempted to index slice from after maximum usize"); 12 | } 13 | 14 | #[cold] 15 | #[inline(never)] 16 | #[track_caller] 17 | pub(crate) const fn slice_end_index_overflow_fail() -> ! { 18 | panic!("attempted to index slice up to maximum usize"); 19 | } 20 | 21 | #[cold] 22 | #[inline(never)] 23 | #[track_caller] 24 | fn slice_index_order_fail(index: usize, end: usize) -> ! { 25 | panic!("slice index starts at {index} but ends at {end}"); 26 | } 27 | 28 | #[cold] 29 | #[inline(never)] 30 | #[track_caller] 31 | fn slice_end_index_len_fail(index: usize, len: usize) -> ! { 32 | panic!("range end index {index} out of range for slice of length {len}") 33 | } 34 | 35 | /// See [`std::slice::range`]. 36 | #[track_caller] 37 | #[must_use] 38 | pub(crate) fn range(range: R, bounds: ops::RangeTo) -> ops::Range 39 | where 40 | R: ops::RangeBounds, 41 | { 42 | let len = bounds.end; 43 | 44 | let start: ops::Bound<&usize> = range.start_bound(); 45 | let start = match start { 46 | ops::Bound::Included(&start) => start, 47 | ops::Bound::Excluded(start) => start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail()), 48 | ops::Bound::Unbounded => 0, 49 | }; 50 | 51 | let end: ops::Bound<&usize> = range.end_bound(); 52 | let end = match end { 53 | ops::Bound::Included(end) => end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail()), 54 | ops::Bound::Excluded(&end) => end, 55 | ops::Bound::Unbounded => len, 56 | }; 57 | 58 | if start > end { 59 | slice_index_order_fail(start, end); 60 | } 61 | if end > len { 62 | slice_end_index_len_fail(end, len); 63 | } 64 | 65 | ops::Range { start, end } 66 | } 67 | 68 | /// See [`slice::split_at_unchecked`]. 69 | #[inline] 70 | #[must_use] 71 | pub(crate) unsafe fn split_at_unchecked(slice: &[T], mid: usize) -> (&[T], &[T]) { 72 | // STD-FIXME(const-hack): the const function `from_raw_parts` is used to make this 73 | // function const; previously the implementation used 74 | // `(self.get_unchecked(..mid), self.get_unchecked(mid..))` 75 | 76 | let len = slice.len(); 77 | let ptr = slice.as_ptr(); 78 | 79 | debug_assert!( 80 | mid <= len, 81 | "slice::split_at_unchecked requires the index to be within the slice" 82 | ); 83 | 84 | // SAFETY: Caller has to check that `0 <= mid <= self.len()` 85 | unsafe { 86 | ( 87 | from_raw_parts(ptr, mid), 88 | from_raw_parts(ptr.add(mid), unchecked_sub(len, mid)), 89 | ) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/polyfill/str.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod lossy; 2 | pub(crate) mod validations; 3 | -------------------------------------------------------------------------------- /src/polyfill/str/validations.rs: -------------------------------------------------------------------------------- 1 | // https://tools.ietf.org/html/rfc3629 2 | const UTF8_CHAR_WIDTH: &[u8; 256] = &[ 3 | // 1 2 3 4 5 6 7 8 9 A B C D E F 4 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 5 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 6 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 7 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 8 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 9 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 10 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 11 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 12 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 13 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 14 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 15 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 16 | 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C 17 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // D 18 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // E 19 | 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // F 20 | ]; 21 | 22 | /// Given a first byte, determines how many bytes are in this UTF-8 character. 23 | #[must_use] 24 | #[inline] 25 | pub(crate) const fn utf8_char_width(b: u8) -> usize { 26 | UTF8_CHAR_WIDTH[b as usize] as usize 27 | } 28 | -------------------------------------------------------------------------------- /src/polyfill/usize.rs: -------------------------------------------------------------------------------- 1 | /// See [`usize::unchecked_mul`]. 2 | #[inline(always)] 3 | pub(crate) unsafe fn unchecked_mul(lhs: usize, rhs: usize) -> usize { 4 | lhs * rhs 5 | } 6 | 7 | /// See [`usize::unchecked_sub`]. 8 | #[inline(always)] 9 | pub(crate) unsafe fn unchecked_sub(lhs: usize, rhs: usize) -> usize { 10 | lhs - rhs 11 | } 12 | -------------------------------------------------------------------------------- /src/raw_bump_box.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | marker::PhantomData, 3 | mem::{self, transmute}, 4 | ptr::{self, NonNull}, 5 | }; 6 | 7 | use crate::{ 8 | polyfill::{non_null, transmute_mut, transmute_ref}, 9 | BumpBox, 10 | }; 11 | 12 | /// Like [`BumpBox`] but without its lifetime. 13 | #[repr(transparent)] 14 | pub struct RawBumpBox { 15 | pub(crate) ptr: NonNull, 16 | 17 | /// Marks ownership over T. () 18 | marker: PhantomData, 19 | } 20 | 21 | unsafe impl Send for RawBumpBox {} 22 | unsafe impl Sync for RawBumpBox {} 23 | 24 | impl Drop for RawBumpBox { 25 | #[inline(always)] 26 | fn drop(&mut self) { 27 | unsafe { self.ptr.as_ptr().drop_in_place() } 28 | } 29 | } 30 | 31 | impl RawBumpBox { 32 | #[allow(dead_code)] 33 | #[inline(always)] 34 | pub(crate) unsafe fn cook<'a>(self) -> BumpBox<'a, T> { 35 | transmute(self) 36 | } 37 | 38 | #[allow(dead_code)] 39 | #[inline(always)] 40 | pub(crate) unsafe fn cook_ref<'a>(&self) -> &BumpBox<'a, T> { 41 | transmute_ref(self) 42 | } 43 | 44 | #[allow(dead_code)] 45 | #[inline(always)] 46 | pub(crate) unsafe fn cook_mut<'a>(&mut self) -> &mut BumpBox<'a, T> { 47 | transmute_mut(self) 48 | } 49 | 50 | #[inline(always)] 51 | pub(crate) unsafe fn from_cooked(cooked: BumpBox<'_, T>) -> Self { 52 | Self { 53 | ptr: cooked.into_raw(), 54 | marker: PhantomData, 55 | } 56 | } 57 | 58 | #[inline(always)] 59 | pub(crate) const unsafe fn from_ptr(ptr: NonNull) -> Self { 60 | Self { 61 | ptr, 62 | marker: PhantomData, 63 | } 64 | } 65 | 66 | #[inline(always)] 67 | pub(crate) fn into_ptr(self) -> NonNull { 68 | let ptr = unsafe { ptr::read(&self.ptr) }; 69 | mem::forget(self); 70 | ptr 71 | } 72 | 73 | #[must_use] 74 | #[inline(always)] 75 | pub(crate) const fn as_ptr(&self) -> *const T { 76 | self.ptr.as_ptr() 77 | } 78 | 79 | #[must_use] 80 | #[inline(always)] 81 | pub(crate) fn as_mut_ptr(&mut self) -> *mut T { 82 | self.ptr.as_ptr() 83 | } 84 | 85 | #[must_use] 86 | #[inline(always)] 87 | pub const fn as_non_null(&self) -> NonNull { 88 | self.ptr 89 | } 90 | } 91 | 92 | impl RawBumpBox<[T]> { 93 | pub(crate) const EMPTY: Self = Self { 94 | ptr: non_null::slice_from_raw_parts(NonNull::dangling(), 0), 95 | marker: PhantomData, 96 | }; 97 | 98 | #[inline(always)] 99 | pub(crate) const fn len(&self) -> usize { 100 | self.ptr.len() 101 | } 102 | 103 | #[inline(always)] 104 | pub(crate) unsafe fn set_ptr(&mut self, new_ptr: NonNull) { 105 | non_null::set_ptr(&mut self.ptr, new_ptr); 106 | } 107 | 108 | #[inline(always)] 109 | pub(crate) unsafe fn set_len(&mut self, new_len: usize) { 110 | non_null::set_len(&mut self.ptr, new_len); 111 | } 112 | } 113 | 114 | impl RawBumpBox { 115 | pub(crate) const EMPTY_STR: Self = Self { 116 | ptr: non_null::str_from_utf8(non_null::slice_from_raw_parts(NonNull::dangling(), 0)), 117 | marker: PhantomData, 118 | }; 119 | 120 | #[inline(always)] 121 | #[allow(dead_code)] 122 | pub(crate) const fn len(&self) -> usize { 123 | non_null::str_bytes(self.ptr).len() 124 | } 125 | 126 | #[inline(always)] 127 | #[allow(dead_code)] 128 | pub(crate) unsafe fn set_ptr(&mut self, new_ptr: NonNull) { 129 | let len = self.len(); 130 | self.ptr = non_null::str_from_utf8(non_null::slice_from_raw_parts(new_ptr, len)); 131 | } 132 | 133 | #[inline(always)] 134 | pub(crate) unsafe fn set_len(&mut self, new_len: usize) { 135 | let ptr = self.ptr.cast::(); 136 | self.ptr = non_null::str_from_utf8(non_null::slice_from_raw_parts(ptr, new_len)); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/raw_fixed_bump_string.rs: -------------------------------------------------------------------------------- 1 | use core::{mem::transmute, ptr::NonNull}; 2 | 3 | use crate::{ 4 | error_behavior::ErrorBehavior, 5 | polyfill::{non_null, transmute_mut, transmute_ref}, 6 | raw_bump_box::RawBumpBox, 7 | BumpAllocator, FixedBumpString, MutBumpAllocator, 8 | }; 9 | 10 | /// Like [`FixedBumpVec`](crate::FixedBumpVec) but without its lifetime. 11 | #[repr(C)] 12 | pub struct RawFixedBumpString { 13 | initialized: RawBumpBox, 14 | capacity: usize, 15 | } 16 | 17 | impl RawFixedBumpString { 18 | pub(crate) const EMPTY: Self = RawFixedBumpString { 19 | initialized: RawBumpBox::EMPTY_STR, 20 | capacity: 0, 21 | }; 22 | 23 | #[inline(always)] 24 | pub(crate) const unsafe fn cook<'a>(self) -> FixedBumpString<'a> { 25 | transmute(self) 26 | } 27 | 28 | #[inline(always)] 29 | pub(crate) const unsafe fn cook_ref<'a>(&self) -> &FixedBumpString<'a> { 30 | transmute_ref(self) 31 | } 32 | 33 | #[inline(always)] 34 | pub(crate) unsafe fn cook_mut<'a>(&mut self) -> &mut FixedBumpString<'a> { 35 | transmute_mut(self) 36 | } 37 | 38 | #[inline(always)] 39 | pub(crate) unsafe fn from_cooked(cooked: FixedBumpString<'_>) -> Self { 40 | let capacity = cooked.capacity(); 41 | let initialized = cooked.into_boxed_str(); 42 | let initialized = RawBumpBox::from_cooked(initialized); 43 | Self { initialized, capacity } 44 | } 45 | 46 | #[inline(always)] 47 | pub(crate) unsafe fn allocate(allocator: &impl BumpAllocator, len: usize) -> Result { 48 | let ptr = B::allocate_slice::(allocator, len)?; 49 | let initialized = RawBumpBox::from_ptr(non_null::str_from_utf8(non_null::slice_from_raw_parts(ptr, 0))); 50 | Ok(Self { 51 | initialized, 52 | capacity: len, 53 | }) 54 | } 55 | 56 | #[inline(always)] 57 | pub(crate) unsafe fn prepare_allocation( 58 | allocator: &mut impl MutBumpAllocator, 59 | len: usize, 60 | ) -> Result { 61 | let allocation = B::prepare_slice_allocation::(allocator, len)?; 62 | let initialized = RawBumpBox::from_ptr(non_null::str_from_utf8(non_null::slice_from_raw_parts( 63 | non_null::as_non_null_ptr(allocation), 64 | 0, 65 | ))); 66 | 67 | Ok(Self { 68 | initialized, 69 | capacity: allocation.len(), 70 | }) 71 | } 72 | 73 | #[inline(always)] 74 | pub(crate) const fn len(&self) -> usize { 75 | non_null::str_bytes(self.initialized.as_non_null()).len() 76 | } 77 | 78 | #[inline(always)] 79 | pub(crate) const fn capacity(&self) -> usize { 80 | self.capacity 81 | } 82 | 83 | #[inline(always)] 84 | pub(crate) fn as_ptr(&self) -> *const u8 { 85 | self.initialized.as_non_null().as_ptr().cast() 86 | } 87 | 88 | #[inline(always)] 89 | pub(crate) fn as_mut_ptr(&mut self) -> *mut u8 { 90 | self.initialized.as_non_null().as_ptr().cast() 91 | } 92 | 93 | #[must_use] 94 | #[inline(always)] 95 | pub const fn as_non_null(&self) -> NonNull { 96 | self.initialized.as_non_null().cast() 97 | } 98 | 99 | #[allow(dead_code)] 100 | #[inline(always)] 101 | pub(crate) unsafe fn set_ptr(&mut self, new_ptr: NonNull) { 102 | self.initialized.set_ptr(new_ptr); 103 | } 104 | 105 | #[inline(always)] 106 | pub(crate) unsafe fn set_len(&mut self, new_len: usize) { 107 | self.initialized.set_len(new_len); 108 | } 109 | 110 | #[inline(always)] 111 | #[allow(dead_code)] 112 | pub(crate) unsafe fn set_cap(&mut self, new_cap: usize) { 113 | self.capacity = new_cap; 114 | } 115 | 116 | #[inline(always)] 117 | #[allow(dead_code)] 118 | pub(crate) fn into_raw_parts(self) -> (NonNull, usize) { 119 | let Self { initialized, capacity } = self; 120 | (initialized.into_ptr(), capacity) 121 | } 122 | 123 | #[inline(always)] 124 | #[cfg(feature = "panic-on-alloc")] 125 | pub(crate) unsafe fn from_raw_parts(slice: NonNull, capacity: usize) -> Self { 126 | Self { 127 | initialized: RawBumpBox::from_ptr(slice), 128 | capacity, 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/raw_fixed_bump_vec.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | mem::transmute, 3 | ptr::{self, NonNull}, 4 | }; 5 | 6 | use crate::{ 7 | error_behavior::ErrorBehavior, 8 | polyfill::{non_null, transmute_mut, transmute_ref}, 9 | raw_bump_box::RawBumpBox, 10 | set_len_on_drop_by_ptr::SetLenOnDropByPtr, 11 | BumpAllocator, FixedBumpVec, MutBumpAllocator, SizedTypeProperties, 12 | }; 13 | 14 | /// Like [`FixedBumpVec`] but without its lifetime. 15 | #[repr(C)] 16 | pub struct RawFixedBumpVec { 17 | initialized: RawBumpBox<[T]>, 18 | capacity: usize, 19 | } 20 | 21 | impl RawFixedBumpVec { 22 | pub(crate) const EMPTY: Self = RawFixedBumpVec { 23 | initialized: RawBumpBox::EMPTY, 24 | capacity: if T::IS_ZST { usize::MAX } else { 0 }, 25 | }; 26 | 27 | pub(crate) const unsafe fn new_zst(len: usize) -> Self { 28 | assert!(T::IS_ZST); 29 | 30 | RawFixedBumpVec { 31 | initialized: unsafe { RawBumpBox::from_ptr(non_null::slice_from_raw_parts(NonNull::dangling(), len)) }, 32 | capacity: usize::MAX, 33 | } 34 | } 35 | 36 | #[inline(always)] 37 | pub(crate) const unsafe fn cook<'a>(self) -> FixedBumpVec<'a, T> { 38 | transmute(self) 39 | } 40 | 41 | #[inline(always)] 42 | pub(crate) const unsafe fn cook_ref<'a>(&self) -> &FixedBumpVec<'a, T> { 43 | transmute_ref(self) 44 | } 45 | 46 | #[inline(always)] 47 | pub(crate) unsafe fn cook_mut<'a>(&mut self) -> &mut FixedBumpVec<'a, T> { 48 | transmute_mut(self) 49 | } 50 | 51 | #[inline(always)] 52 | pub(crate) unsafe fn from_cooked(cooked: FixedBumpVec<'_, T>) -> Self { 53 | let (initialized, capacity) = cooked.into_raw_parts(); 54 | let initialized = RawBumpBox::from_cooked(initialized); 55 | Self { initialized, capacity } 56 | } 57 | 58 | #[inline(always)] 59 | pub(crate) unsafe fn allocate(allocator: &impl BumpAllocator, len: usize) -> Result { 60 | let ptr = B::allocate_slice::(allocator, len)?; 61 | 62 | Ok(Self { 63 | initialized: RawBumpBox::from_ptr(non_null::slice_from_raw_parts(ptr, 0)), 64 | capacity: len, 65 | }) 66 | } 67 | 68 | #[inline(always)] 69 | pub(crate) unsafe fn prepare_allocation( 70 | allocator: &mut impl MutBumpAllocator, 71 | len: usize, 72 | ) -> Result { 73 | let allocation = B::prepare_slice_allocation::(allocator, len)?; 74 | 75 | Ok(Self { 76 | initialized: RawBumpBox::from_ptr(non_null::slice_from_raw_parts(non_null::as_non_null_ptr(allocation), 0)), 77 | capacity: allocation.len(), 78 | }) 79 | } 80 | 81 | /// `new_cap` must be greater than `self.capacity` 82 | pub(crate) unsafe fn grow_prepared_allocation( 83 | &mut self, 84 | allocator: &mut impl MutBumpAllocator, 85 | minimum_new_cap: usize, 86 | ) -> Result<(), B> { 87 | debug_assert!(minimum_new_cap > self.capacity); 88 | let allocation = B::prepare_slice_allocation::(allocator, minimum_new_cap)?; 89 | 90 | let new_ptr = allocation.cast::(); 91 | let new_cap = allocation.len(); 92 | 93 | ptr::copy_nonoverlapping(self.as_ptr(), new_ptr.as_ptr(), self.len()); 94 | 95 | self.initialized.set_ptr(new_ptr); 96 | self.capacity = new_cap; 97 | 98 | Ok(()) 99 | } 100 | 101 | #[inline(always)] 102 | pub(crate) const fn len(&self) -> usize { 103 | self.initialized.len() 104 | } 105 | 106 | #[inline(always)] 107 | pub(crate) const fn capacity(&self) -> usize { 108 | self.capacity 109 | } 110 | 111 | #[inline(always)] 112 | pub(crate) fn as_ptr(&self) -> *const T { 113 | self.initialized.as_ptr().cast() 114 | } 115 | 116 | #[inline(always)] 117 | pub(crate) fn as_mut_ptr(&mut self) -> *mut T { 118 | self.initialized.as_mut_ptr().cast() 119 | } 120 | 121 | #[must_use] 122 | #[inline(always)] 123 | pub const fn as_non_null(&self) -> NonNull { 124 | self.initialized.as_non_null().cast() 125 | } 126 | 127 | #[doc(hidden)] 128 | #[deprecated = "too niche; compute this yourself if needed"] 129 | #[must_use] 130 | #[inline(always)] 131 | pub fn as_non_null_slice(&self) -> NonNull<[T]> { 132 | self.initialized.as_non_null() 133 | } 134 | 135 | #[inline(always)] 136 | pub(crate) unsafe fn set_ptr(&mut self, new_ptr: NonNull) { 137 | self.initialized.set_ptr(new_ptr); 138 | } 139 | 140 | #[inline(always)] 141 | pub(crate) unsafe fn set_len(&mut self, new_len: usize) { 142 | self.initialized.set_len(new_len); 143 | } 144 | 145 | #[inline(always)] 146 | pub(crate) unsafe fn set_cap(&mut self, new_cap: usize) { 147 | self.capacity = new_cap; 148 | } 149 | 150 | #[inline(always)] 151 | pub(crate) unsafe fn set_len_on_drop(&mut self) -> SetLenOnDropByPtr { 152 | SetLenOnDropByPtr::new(&mut self.initialized.ptr) 153 | } 154 | 155 | #[inline(always)] 156 | pub(crate) fn into_raw_parts(self) -> (NonNull<[T]>, usize) { 157 | let Self { initialized, capacity } = self; 158 | (initialized.into_ptr(), capacity) 159 | } 160 | 161 | #[inline(always)] 162 | pub(crate) unsafe fn from_raw_parts(slice: NonNull<[T]>, capacity: usize) -> Self { 163 | Self { 164 | initialized: RawBumpBox::from_ptr(slice), 165 | capacity, 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/set_len_on_drop.rs: -------------------------------------------------------------------------------- 1 | // Set the length of the vector when the `SetLenOnDrop` value goes out of scope. 2 | // 3 | // The idea is: The length field in SetLenOnDrop is a local variable 4 | // that the optimizer will see does not alias with any stores through the vector's data 5 | // pointer. This is a workaround for alias analysis issue #32155 6 | pub(super) struct SetLenOnDrop<'a> { 7 | len: &'a mut usize, 8 | local_len: usize, 9 | } 10 | 11 | impl<'a> SetLenOnDrop<'a> { 12 | #[inline] 13 | pub(super) fn new(len: &'a mut usize) -> Self { 14 | SetLenOnDrop { local_len: *len, len } 15 | } 16 | 17 | #[inline] 18 | pub(super) fn increment_len(&mut self, increment: usize) { 19 | self.local_len += increment; 20 | } 21 | 22 | #[inline] 23 | pub(super) fn current_len(&self) -> usize { 24 | self.local_len 25 | } 26 | } 27 | 28 | impl Drop for SetLenOnDrop<'_> { 29 | #[inline] 30 | fn drop(&mut self) { 31 | *self.len = self.local_len; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/set_len_on_drop_by_ptr.rs: -------------------------------------------------------------------------------- 1 | use core::ptr::NonNull; 2 | 3 | use crate::polyfill::non_null; 4 | 5 | // Set the length of the vector when the `SetLenOnDropByPtr` value goes out of scope. 6 | // 7 | // The idea is: The length field in SetLenOnDropByPtr is a local variable 8 | // that the optimizer will see does not alias with any stores through the vector's data 9 | // pointer. This is a workaround for alias analysis issue #32155 10 | pub(super) struct SetLenOnDropByPtr<'a, T> { 11 | slice: &'a mut NonNull<[T]>, 12 | local_len: usize, 13 | } 14 | 15 | impl<'a, T> SetLenOnDropByPtr<'a, T> { 16 | #[inline] 17 | pub(super) fn new(slice: &'a mut NonNull<[T]>) -> Self { 18 | SetLenOnDropByPtr { 19 | local_len: slice.len(), 20 | slice, 21 | } 22 | } 23 | 24 | #[inline] 25 | pub(super) fn increment_len(&mut self, increment: usize) { 26 | self.local_len += increment; 27 | } 28 | 29 | #[inline] 30 | pub(super) fn current_len(&self) -> usize { 31 | self.local_len 32 | } 33 | } 34 | 35 | impl Drop for SetLenOnDropByPtr<'_, T> { 36 | #[inline] 37 | fn drop(&mut self) { 38 | non_null::set_len(self.slice, self.local_len); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/tests/alloc_cstr.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | 3 | use crate::{alloc::Global, Bump}; 4 | 5 | use super::either_way; 6 | 7 | either_way! { 8 | simple 9 | from_str 10 | empty 11 | fmt 12 | interior_null_from_str 13 | interior_null_fmt 14 | interior_null_fmt_mut 15 | } 16 | 17 | fn simple() { 18 | let bump: Bump = Bump::new(); 19 | let input = c"123456789"; 20 | let allocated = bump.alloc_cstr(input); 21 | assert_eq!(allocated, input); 22 | assert_eq!(bump.stats().allocated(), 10); 23 | } 24 | 25 | fn from_str() { 26 | let bump: Bump = Bump::new(); 27 | let input = "123456789"; 28 | let expected = c"123456789"; 29 | let allocated = bump.alloc_cstr_from_str(input); 30 | assert_eq!(allocated, expected); 31 | assert_eq!(bump.stats().allocated(), 10); 32 | } 33 | 34 | fn empty() { 35 | let bump: Bump = Bump::new(); 36 | let input = c""; 37 | let allocated = bump.alloc_cstr(input); 38 | assert_eq!(allocated, input); 39 | assert_eq!(bump.stats().allocated(), 1); 40 | } 41 | 42 | fn fmt() { 43 | let bump: Bump = Bump::new(); 44 | let allocated = bump.alloc_cstr_fmt(format_args!("1 + 2 = {}", 1 + 2)); 45 | assert_eq!(allocated, c"1 + 2 = 3"); 46 | assert_eq!(bump.stats().allocated(), 10); 47 | } 48 | 49 | fn interior_null_from_str() { 50 | let bump: Bump = Bump::new(); 51 | let input = "hello\0world"; 52 | let allocated = bump.alloc_cstr_from_str(input); 53 | assert_eq!(allocated, c"hello"); 54 | assert_eq!(bump.stats().allocated(), 6); 55 | } 56 | 57 | fn interior_null_fmt() { 58 | let bump: Bump = Bump::new(); 59 | let hello = "hello"; 60 | let world = "world"; 61 | let allocated = bump.alloc_cstr_fmt(assert_multiple(format_args!("{hello}\0{world}"))); 62 | assert_eq!(allocated, c"hello"); 63 | assert_eq!(allocated.to_bytes(), b"hello"); 64 | assert_eq!(bump.stats().allocated(), 6); 65 | } 66 | 67 | fn interior_null_fmt_mut() { 68 | let mut bump: Bump = Bump::new(); 69 | let hello = "hello"; 70 | let world = "world"; 71 | let allocated = bump.alloc_cstr_fmt_mut(assert_multiple(format_args!("{hello}\0{world}"))); 72 | assert_eq!(allocated, c"hello"); 73 | assert_eq!(allocated.to_bytes(), b"hello"); 74 | assert_eq!(bump.stats().allocated(), 6); 75 | } 76 | 77 | fn assert_multiple(args: fmt::Arguments) -> fmt::Arguments { 78 | assert!(args.as_str().is_none(), "expected multiple format arguments"); 79 | args 80 | } 81 | -------------------------------------------------------------------------------- /src/tests/alloc_fmt.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | alloc::Layout, 3 | fmt::{Debug, Display}, 4 | ptr::NonNull, 5 | }; 6 | 7 | use crate::{ 8 | alloc::{AllocError, Allocator, Global}, 9 | bump_format, mut_bump_format, Bump, 10 | }; 11 | 12 | use super::either_way; 13 | 14 | fn nothing() { 15 | struct Nothing; 16 | 17 | impl Display for Nothing { 18 | fn fmt(&self, _: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 19 | Ok(()) 20 | } 21 | } 22 | 23 | let mut bump: Bump = Bump::new(); 24 | 25 | bump.alloc_fmt(format_args!("{Nothing}")); 26 | bump.alloc_fmt_mut(format_args!("{Nothing}")); 27 | 28 | assert_eq!(bump.stats().allocated(), 0); 29 | } 30 | 31 | fn nothing_extra() { 32 | let bump: Bump = Bump::new(); 33 | let string = bump.alloc_fmt(format_args!("ext{Nothing}ra")); 34 | assert_eq!(string, "extra"); 35 | assert_eq!(bump.stats().allocated(), 5); 36 | } 37 | 38 | fn nothing_extra_mut() { 39 | let mut bump: Bump = Bump::new(); 40 | let string = bump.alloc_fmt_mut(format_args!("ext{Nothing}ra")); 41 | assert_eq!(string, "extra"); 42 | drop(string); 43 | assert_eq!(bump.stats().allocated(), 5); 44 | } 45 | 46 | struct Nothing; 47 | 48 | impl Display for Nothing { 49 | fn fmt(&self, _: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 50 | Ok(()) 51 | } 52 | } 53 | 54 | fn three() { 55 | let bump: Bump = Bump::new(); 56 | bump.alloc_fmt(format_args!("{}", 3.1)); 57 | assert_eq!(bump.stats().allocated(), 3); 58 | } 59 | 60 | fn three_mut() { 61 | let mut bump: Bump = Bump::new(); 62 | bump.alloc_fmt_mut(format_args!("{}", 3.1)); 63 | assert_eq!(bump.stats().allocated(), 3); 64 | } 65 | 66 | struct ErrorsOnFmt; 67 | 68 | impl Display for ErrorsOnFmt { 69 | fn fmt(&self, _: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 70 | Err(core::fmt::Error) 71 | } 72 | } 73 | 74 | fn trait_panic() { 75 | let bump: Bump = Bump::new(); 76 | bump.alloc_fmt(format_args!("{ErrorsOnFmt}")); 77 | } 78 | 79 | fn trait_panic_mut() { 80 | let mut bump: Bump = Bump::new(); 81 | bump.alloc_fmt_mut(format_args!("{ErrorsOnFmt}")); 82 | } 83 | 84 | fn format_trait_panic() { 85 | let bump: Bump = Bump::new(); 86 | bump_format!(in &bump, "{ErrorsOnFmt}"); 87 | } 88 | 89 | fn format_trait_panic_mut() { 90 | let mut bump: Bump = Bump::new(); 91 | mut_bump_format!(in &mut bump, "{ErrorsOnFmt}"); 92 | } 93 | 94 | either_way! { 95 | nothing 96 | nothing_extra 97 | nothing_extra_mut 98 | three 99 | three_mut 100 | 101 | #[should_panic = "formatting trait implementation returned an error"] 102 | trait_panic 103 | 104 | #[should_panic = "formatting trait implementation returned an error"] 105 | trait_panic_mut 106 | 107 | #[should_panic = "formatting trait implementation returned an error"] 108 | format_trait_panic 109 | 110 | #[should_panic = "formatting trait implementation returned an error"] 111 | format_trait_panic_mut 112 | } 113 | -------------------------------------------------------------------------------- /src/tests/alloc_iter.rs: -------------------------------------------------------------------------------- 1 | use crate::{alloc::Global, Bump}; 2 | 3 | use super::either_way; 4 | 5 | fn zst() { 6 | let mut bump: Bump = Bump::new(); 7 | 8 | bump.alloc_iter([[0u8; 0]; 10]); 9 | bump.alloc_iter([[0u16; 0]; 10]); 10 | bump.alloc_iter([[0u32; 0]; 10]); 11 | bump.alloc_iter([[0u64; 0]; 10]); 12 | 13 | bump.alloc_iter_mut([[0u8; 0]; 10]); 14 | bump.alloc_iter_mut([[0u16; 0]; 10]); 15 | bump.alloc_iter_mut([[0u32; 0]; 10]); 16 | bump.alloc_iter_mut([[0u64; 0]; 10]); 17 | 18 | bump.alloc_iter_mut_rev([[0u8; 0]; 10]); 19 | bump.alloc_iter_mut_rev([[0u16; 0]; 10]); 20 | bump.alloc_iter_mut_rev([[0u32; 0]; 10]); 21 | bump.alloc_iter_mut_rev([[0u64; 0]; 10]); 22 | 23 | assert_eq!(bump.stats().allocated(), 0); 24 | } 25 | 26 | fn empty() { 27 | let mut bump: Bump = Bump::new(); 28 | 29 | bump.alloc_iter(core::iter::empty::()); 30 | bump.alloc_iter(core::iter::empty::()); 31 | bump.alloc_iter(core::iter::empty::()); 32 | bump.alloc_iter(core::iter::empty::()); 33 | 34 | bump.alloc_iter_mut(core::iter::empty::()); 35 | bump.alloc_iter_mut(core::iter::empty::()); 36 | bump.alloc_iter_mut(core::iter::empty::()); 37 | bump.alloc_iter_mut(core::iter::empty::()); 38 | 39 | bump.alloc_iter_mut_rev(core::iter::empty::()); 40 | bump.alloc_iter_mut_rev(core::iter::empty::()); 41 | bump.alloc_iter_mut_rev(core::iter::empty::()); 42 | bump.alloc_iter_mut_rev(core::iter::empty::()); 43 | 44 | assert_eq!(bump.stats().allocated(), 0); 45 | } 46 | 47 | fn three() { 48 | let bump: Bump = Bump::new(); 49 | bump.alloc_iter(SuppressHints([1, 2, 3].into_iter())); 50 | assert_eq!(bump.stats().allocated(), 3 * 4); 51 | } 52 | 53 | fn three_mut() { 54 | let mut bump: Bump = Bump::new(); 55 | bump.alloc_iter_mut(SuppressHints([1, 2, 3].into_iter())); 56 | assert_eq!(bump.stats().allocated(), 3 * 4); 57 | } 58 | 59 | fn three_mut_rev() { 60 | let mut bump: Bump = Bump::new(); 61 | bump.alloc_iter_mut_rev(SuppressHints([1, 2, 3].into_iter())); 62 | assert_eq!(bump.stats().allocated(), 3 * 4); 63 | } 64 | 65 | // so as not to give `BumpVec` the correct capacity before iteration via `size_hint` 66 | struct SuppressHints(T); 67 | 68 | impl Iterator for SuppressHints { 69 | type Item = T::Item; 70 | 71 | fn next(&mut self) -> Option { 72 | T::next(&mut self.0) 73 | } 74 | } 75 | 76 | either_way! { 77 | zst 78 | empty 79 | three 80 | three_mut 81 | three_mut_rev 82 | } 83 | -------------------------------------------------------------------------------- /src/tests/alloc_slice.rs: -------------------------------------------------------------------------------- 1 | use std::string::{String, ToString}; 2 | 3 | use crate::{alloc::Global, Bump}; 4 | 5 | use super::either_way; 6 | 7 | fn zst() { 8 | const ZST: [u64; 0] = [0u64; 0]; 9 | 10 | let bump: Bump = Bump::new(); 11 | 12 | bump.alloc_slice_copy(&[ZST; 10]); 13 | bump.alloc_slice_clone(&[ZST; 10]); 14 | bump.alloc_slice_fill(10, ZST); 15 | bump.alloc_slice_fill_with(10, || ZST); 16 | 17 | assert_eq!(bump.stats().allocated(), 0); 18 | } 19 | 20 | fn empty() { 21 | let bump: Bump = Bump::new(); 22 | 23 | bump.alloc_slice_copy::(&[]); 24 | bump.alloc_slice_clone::(&[]); 25 | bump.alloc_slice_fill_with(0, || -> String { panic!("should not happen") }); 26 | bump.alloc_slice_fill(0, 42u64); 27 | bump.alloc_slice_fill(0, &"hello".to_string()); 28 | bump.alloc_slice_fill_with(0, String::default); 29 | 30 | assert_eq!(bump.stats().allocated(), 0); 31 | } 32 | 33 | fn overflow() { 34 | let bump: Bump = Bump::new(); 35 | bump.alloc_slice_fill_with(usize::MAX, u64::default); 36 | } 37 | 38 | either_way! { 39 | zst 40 | empty 41 | 42 | #[should_panic(expected = "capacity overflow")] 43 | overflow 44 | } 45 | -------------------------------------------------------------------------------- /src/tests/alloc_try_with.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | dbg, 3 | mem::{self, offset_of}, 4 | }; 5 | 6 | use crate::{alloc::Global, Bump}; 7 | 8 | use super::either_way; 9 | 10 | macro_rules! assert_allocated { 11 | ($bump:ident, $expected:expr) => { 12 | let expected = $expected; 13 | let count = $bump.stats().count(); 14 | let allocated = $bump.stats().allocated(); 15 | assert_eq!( 16 | count, 1, 17 | "there are multiple chunks allocated ({count}), \ 18 | this makes `assert_allocated_range` misleading" 19 | ); 20 | assert_eq!( 21 | allocated, expected, 22 | "expected allocated bytes of {expected} but got {allocated}" 23 | ); 24 | }; 25 | } 26 | 27 | const SIZE: usize = 1024 * 10; 28 | 29 | fn zeroes() -> [u32; N] { 30 | [0; N] 31 | } 32 | 33 | type TestOk = [u32; 32]; 34 | type TestErr = [u32; 128]; 35 | type TestResult = Result; 36 | 37 | #[cfg(feature = "nightly-tests")] 38 | fn basic_ok() { 39 | let bump: Bump = Bump::with_size(SIZE); 40 | _ = bump.alloc_try_with(|| -> TestResult { Ok(zeroes()) }); 41 | 42 | if UP { 43 | let expected = 32 * 4 + offset_of!(TestResult, Ok.0); 44 | assert_allocated!(bump, expected); 45 | } else { 46 | let expected = size_of::() - offset_of!(TestResult, Ok.0); 47 | assert_allocated!(bump, expected); 48 | } 49 | } 50 | 51 | #[cfg(feature = "nightly-tests")] 52 | fn basic_ok_mut() { 53 | let mut bump: Bump = Bump::with_size(SIZE); 54 | _ = bump.alloc_try_with_mut(|| -> TestResult { Ok(zeroes()) }); 55 | 56 | if UP { 57 | let expected = 32 * 4 + offset_of!(TestResult, Ok.0); 58 | assert_allocated!(bump, expected); 59 | } else { 60 | let expected = size_of::() - offset_of!(TestResult, Ok.0); 61 | assert_allocated!(bump, expected); 62 | } 63 | } 64 | 65 | fn basic_err() { 66 | let bump: Bump = Bump::with_size(SIZE); 67 | _ = bump.alloc_try_with(|| -> Result<[u32; 32], [u32; 128]> { Err(zeroes()) }); 68 | assert_eq!(bump.stats().allocated(), 0); 69 | } 70 | 71 | fn basic_err_mut() { 72 | let mut bump: Bump = Bump::with_size(SIZE); 73 | _ = bump.alloc_try_with_mut(|| -> Result<[u32; 32], [u32; 128]> { Err(zeroes()) }); 74 | assert_eq!(bump.stats().allocated(), 0); 75 | } 76 | 77 | fn alloc_in_closure_ok() { 78 | let bump: Bump = Bump::with_size(SIZE); 79 | 80 | _ = bump.alloc_try_with(|| -> Result<[u32; 32], [u32; 128]> { 81 | bump.alloc(0u8); 82 | Ok(zeroes()) 83 | }); 84 | 85 | let expected = size_of::() + 1; 86 | assert_allocated!(bump, expected); 87 | } 88 | 89 | fn alloc_in_closure_err() { 90 | let bump: Bump = Bump::with_size(SIZE); 91 | 92 | _ = bump.alloc_try_with(|| -> Result<[u32; 32], [u32; 128]> { 93 | bump.alloc(0u8); 94 | Err(zeroes()) 95 | }); 96 | 97 | let expected = size_of::() + 1; 98 | assert_allocated!(bump, expected); 99 | } 100 | 101 | #[test] 102 | fn wat() { 103 | let bump: Bump = Bump::new(); 104 | bump.alloc(0u32); 105 | dbg!(bump.stats().allocated()); 106 | } 107 | 108 | either_way! { 109 | #[cfg(feature = "nightly-tests")] 110 | basic_ok 111 | #[cfg(feature = "nightly-tests")] 112 | basic_ok_mut 113 | basic_err 114 | basic_err_mut 115 | alloc_in_closure_ok 116 | alloc_in_closure_err 117 | } 118 | -------------------------------------------------------------------------------- /src/tests/allocator_api.rs: -------------------------------------------------------------------------------- 1 | use std::{alloc::Layout, ptr::NonNull}; 2 | 3 | use crate::{ 4 | alloc::{Allocator, Global}, 5 | polyfill::non_null, 6 | Bump, 7 | }; 8 | 9 | use super::either_way; 10 | 11 | either_way! { 12 | grow 13 | 14 | grow_last_in_place 15 | 16 | grow_last_out_of_place 17 | } 18 | 19 | fn layout(size: usize) -> Layout { 20 | Layout::from_size_align(size, 4).unwrap() 21 | } 22 | 23 | fn assert_aligned_to(ptr: NonNull<[u8]>) { 24 | assert!(non_null::addr(ptr.cast::()).get() % 4 == 0); 25 | } 26 | 27 | fn grow() { 28 | let bump: Bump = Bump::new(); 29 | 30 | let ptr = bump.allocate(layout(1)).unwrap(); 31 | assert_aligned_to(ptr); 32 | 33 | assert_ne!(ptr, bump.allocate(layout(1)).unwrap()); 34 | 35 | let new = bump.allocate(layout(2048)).unwrap(); 36 | assert_aligned_to(new); 37 | 38 | assert_ne!(ptr.cast::(), new.cast::()); 39 | } 40 | 41 | fn grow_last_in_place() { 42 | let bump: Bump = Bump::new(); 43 | 44 | unsafe { 45 | let ptr = bump.allocate(layout(1)).unwrap(); 46 | assert_aligned_to(ptr); 47 | 48 | let new = bump.grow(ptr.cast(), layout(1), layout(2)).unwrap(); 49 | assert_aligned_to(new); 50 | 51 | if UP { 52 | assert_eq!(ptr.cast::(), new.cast::()); 53 | } 54 | } 55 | } 56 | 57 | fn grow_last_out_of_place() { 58 | let bump: Bump = Bump::new(); 59 | 60 | unsafe { 61 | let ptr = bump.allocate(layout(1)).unwrap(); 62 | assert_aligned_to(ptr); 63 | 64 | let new = bump.grow(ptr.cast(), layout(1), layout(2048)).unwrap(); 65 | assert_aligned_to(new); 66 | 67 | assert_ne!(ptr.cast::(), new.cast::()); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/tests/bump_allocator.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | string::{String, ToString}, 3 | vec::Vec, 4 | }; 5 | 6 | use crate::{Bump, BumpAllocator, BumpVec, MutBumpAllocator, MutBumpVec}; 7 | 8 | fn number_strings(numbers: impl IntoIterator) -> impl Iterator { 9 | numbers.into_iter().map(|i| i.to_string()) 10 | } 11 | 12 | #[test] 13 | fn smoke_test() { 14 | fn test(a: A) { 15 | let mut vec = BumpVec::from_iter_in(number_strings(1..=5), a); 16 | vec.extend(number_strings(6..=9)); 17 | } 18 | 19 | fn mut_test(a: A) { 20 | let mut vec = MutBumpVec::from_iter_in(number_strings(1..=5), a); 21 | vec.extend(number_strings(6..=9)); 22 | } 23 | 24 | let mut a: Bump = Bump::new(); 25 | test(&mut a); 26 | test(&a); 27 | test(a); 28 | 29 | let mut a: Bump = Bump::new(); 30 | a.scoped(|mut a| { 31 | test(&mut a); 32 | test(&a); 33 | test(a); 34 | }); 35 | 36 | let mut a: Bump = Bump::new(); 37 | mut_test(&mut a); 38 | mut_test(a); 39 | 40 | let mut a: Bump = Bump::new(); 41 | a.scoped(|mut a| { 42 | mut_test(&mut a); 43 | mut_test(a); 44 | }); 45 | } 46 | -------------------------------------------------------------------------------- /src/tests/bump_string.rs: -------------------------------------------------------------------------------- 1 | use crate::{alloc::Global, Bump, BumpString, BumpVec}; 2 | 3 | use super::either_way; 4 | 5 | either_way! { 6 | shrinks 7 | deallocates 8 | into_str 9 | into_str_without_shrink 10 | } 11 | 12 | fn shrinks() { 13 | let bump: Bump = Bump::new(); 14 | let mut string = BumpString::from_str_in("1234", &bump); 15 | assert_eq!(bump.stats().allocated(), 4); 16 | string.pop(); 17 | string.shrink_to_fit(); 18 | assert_eq!(bump.stats().allocated(), 3); 19 | string.clear(); 20 | string.shrink_to_fit(); 21 | assert_eq!(bump.stats().allocated(), 0); 22 | } 23 | 24 | fn deallocates() { 25 | let bump: Bump = Bump::new(); 26 | let string = BumpString::from_str_in("123", &bump); 27 | assert_eq!(bump.stats().allocated(), 3); 28 | drop(string); 29 | assert_eq!(bump.stats().allocated(), 0); 30 | } 31 | 32 | fn into_str() { 33 | let bump: Bump = Bump::new(); 34 | let mut string = BumpString::from_str_in("12345", &bump); 35 | assert_eq!(bump.stats().allocated(), 5); 36 | string.truncate(3); 37 | let slice = string.into_str(); 38 | assert_eq!(bump.stats().allocated(), 3); 39 | _ = slice; 40 | } 41 | 42 | fn into_str_without_shrink() { 43 | let bump: Bump = Bump::new(); 44 | let mut string = BumpString::from_str_in("12345", &bump); 45 | assert_eq!(bump.stats().allocated(), 5); 46 | string.truncate(3); 47 | let slice = string.into_fixed_string().into_str(); 48 | assert_eq!(bump.stats().allocated(), 5); 49 | _ = slice; 50 | } 51 | -------------------------------------------------------------------------------- /src/tests/bump_vec_doc.rs: -------------------------------------------------------------------------------- 1 | //! doc tests but for up and down 2 | 3 | use crate::{alloc::Global, bump_vec, Bump, BumpVec}; 4 | 5 | use super::either_way; 6 | 7 | either_way! { 8 | new 9 | 10 | from_array 11 | 12 | from_elem 13 | 14 | extend_from_within_copy 15 | 16 | resize 17 | 18 | resize_with 19 | 20 | capacity 21 | 22 | insert 23 | 24 | remove 25 | 26 | swap_remove 27 | 28 | truncate 29 | } 30 | 31 | fn new() { 32 | let bump: Bump = Bump::new(); 33 | let vec: BumpVec = bump_vec![in &bump]; 34 | assert!(vec.is_empty()); 35 | } 36 | 37 | fn from_array() { 38 | let bump: Bump = Bump::new(); 39 | let vec = bump_vec![in ≎ 1, 2, 3]; 40 | assert_eq!(vec[0], 1); 41 | assert_eq!(vec[1], 2); 42 | assert_eq!(vec[2], 3); 43 | } 44 | 45 | fn from_elem() { 46 | let bump: Bump = Bump::new(); 47 | let vec = bump_vec![in ≎ 1; 3]; 48 | assert_eq!(vec, [1, 1, 1]); 49 | } 50 | 51 | fn extend_from_within_copy() { 52 | let bump: Bump = Bump::new(); 53 | 54 | let mut vec = bump_vec![in ≎ 0, 1, 2, 3, 4]; 55 | 56 | vec.extend_from_within_copy(2..); 57 | assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4]); 58 | 59 | vec.extend_from_within_copy(..2); 60 | assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4, 0, 1]); 61 | 62 | vec.extend_from_within_copy(4..8); 63 | assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4, 0, 1, 4, 2, 3, 4]); 64 | } 65 | 66 | fn resize() { 67 | let bump: Bump = Bump::new(); 68 | 69 | let mut vec = bump_vec![in ≎ "hello"]; 70 | vec.resize(3, "world"); 71 | assert_eq!(vec, ["hello", "world", "world"]); 72 | drop(vec); 73 | 74 | let mut vec = bump_vec![in ≎ 1, 2, 3, 4]; 75 | vec.resize(2, 0); 76 | assert_eq!(vec, [1, 2]); 77 | } 78 | 79 | fn resize_with() { 80 | let bump: Bump = Bump::new(); 81 | 82 | let mut vec = bump_vec![in ≎ 1, 2, 3]; 83 | vec.resize_with(5, Default::default); 84 | assert_eq!(vec, [1, 2, 3, 0, 0]); 85 | drop(vec); 86 | 87 | let mut vec = bump_vec![in &bump]; 88 | let mut p = 1; 89 | vec.resize_with(4, || { 90 | p *= 2; 91 | p 92 | }); 93 | assert_eq!(vec, [2, 4, 8, 16]); 94 | } 95 | 96 | fn capacity() { 97 | let bump: Bump = Bump::new(); 98 | 99 | let vec: BumpVec = BumpVec::with_capacity_in(2048, &bump); 100 | assert!(vec.capacity() >= 2048); 101 | } 102 | 103 | fn insert() { 104 | let bump: Bump = Bump::new(); 105 | 106 | let mut vec = bump_vec![in ≎ 1, 2, 3]; 107 | vec.insert(1, 4); 108 | assert_eq!(vec, [1, 4, 2, 3]); 109 | vec.insert(4, 5); 110 | assert_eq!(vec, [1, 4, 2, 3, 5]); 111 | } 112 | 113 | fn remove() { 114 | let bump: Bump = Bump::new(); 115 | 116 | let mut v = bump_vec![in ≎ 1, 2, 3]; 117 | assert_eq!(v.remove(1), 2); 118 | assert_eq!(v, [1, 3]); 119 | } 120 | 121 | fn swap_remove() { 122 | let bump: Bump = Bump::new(); 123 | 124 | let mut v = bump_vec![in ≎ "foo", "bar", "baz", "qux"]; 125 | 126 | assert_eq!(v.swap_remove(1), "bar"); 127 | assert_eq!(v, ["foo", "qux", "baz"]); 128 | 129 | assert_eq!(v.swap_remove(0), "foo"); 130 | assert_eq!(v, ["baz", "qux"]); 131 | } 132 | 133 | fn truncate() { 134 | let bump: Bump = Bump::new(); 135 | 136 | { 137 | let mut vec = bump_vec![in ≎ 1, 2, 3, 4, 5]; 138 | vec.truncate(2); 139 | assert_eq!(vec, [1, 2]); 140 | } 141 | 142 | { 143 | let mut vec = bump_vec![in ≎ 1, 2, 3]; 144 | vec.truncate(8); 145 | assert_eq!(vec, [1, 2, 3]); 146 | } 147 | 148 | { 149 | let mut vec = bump_vec![in ≎ 1, 2, 3]; 150 | vec.truncate(0); 151 | assert!(vec.is_empty()); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/tests/chunk_size.rs: -------------------------------------------------------------------------------- 1 | use core::{alloc::Layout, ptr::NonNull}; 2 | use std::convert::Infallible; 3 | 4 | use crate::{ 5 | alloc::{AllocError, Allocator, Global}, 6 | Bump, 7 | }; 8 | 9 | use super::either_way; 10 | 11 | macro_rules! create_mock_allocator { 12 | ( 13 | $ident:ident $size_and_align:literal 14 | ) => { 15 | #[allow(dead_code)] 16 | #[repr(align($size_and_align))] 17 | #[derive(Clone)] 18 | struct $ident([u8; $size_and_align]); 19 | 20 | impl $ident { 21 | fn new() -> Self { 22 | Self([0; $size_and_align]) 23 | } 24 | } 25 | 26 | unsafe impl Allocator for $ident { 27 | fn allocate(&self, layout: Layout) -> Result, AllocError> { 28 | Global.allocate(layout) 29 | } 30 | 31 | unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { 32 | Global.deallocate(ptr, layout); 33 | } 34 | } 35 | }; 36 | } 37 | 38 | either_way! { 39 | aligned_allocator_issue_32 40 | giant_base_allocator 41 | } 42 | 43 | fn aligned_allocator_issue_32() { 44 | create_mock_allocator! { 45 | BigAllocator 32 46 | } 47 | 48 | let _: Bump<_, 1, false> = Bump::with_size_in(0x2000, BigAllocator::new()); 49 | } 50 | 51 | fn giant_base_allocator() { 52 | create_mock_allocator! { 53 | MyAllocator 4096 54 | } 55 | 56 | let bump: Bump<_, 1, UP> = Bump::with_size_in(0x2000, MyAllocator::new()); 57 | assert_eq!(bump.stats().allocated(), 0); 58 | bump.alloc_str("hey"); 59 | assert_eq!(bump.stats().allocated(), 3); 60 | } 61 | -------------------------------------------------------------------------------- /src/tests/coerce_unsized.rs: -------------------------------------------------------------------------------- 1 | use std::{dbg, fmt::Debug, future::Future}; 2 | 3 | use crate::{Bump, BumpBox}; 4 | 5 | #[test] 6 | fn slice() { 7 | let bump: Bump = Bump::new(); 8 | let slice: BumpBox<[i32]> = bump.alloc([1, 2, 3]); 9 | dbg!(slice); 10 | } 11 | 12 | #[test] 13 | fn object() { 14 | let bump: Bump = Bump::new(); 15 | let slice: BumpBox = bump.alloc([1, 2, 3]); 16 | dbg!(slice); 17 | } 18 | -------------------------------------------------------------------------------- /src/tests/fixed_bump_vec.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::manual_assert)] 2 | 3 | use std::{ 4 | boxed::Box, 5 | dbg, format, 6 | hint::black_box, 7 | string::{String, ToString}, 8 | vec::Vec, 9 | }; 10 | 11 | use crate::{alloc::Global, bump_vec, tests::expect_no_panic, Bump, FixedBumpVec}; 12 | 13 | use super::either_way; 14 | 15 | either_way! { 16 | map_in_place_same_layout 17 | map_in_place_smaller_layout 18 | map_in_place_to_zst 19 | map_in_place_from_zst_to_zst 20 | } 21 | 22 | fn map_in_place_same_layout() { 23 | for panic_on in 0..4 { 24 | let bump: Bump = Bump::new(); 25 | 26 | let result = std::panic::catch_unwind(|| { 27 | let mut i = 1; 28 | let a = FixedBumpVec::::from_iter_exact_in([1, 2, 3].map(|i| i.to_string()), &bump); 29 | assert_eq!(a.capacity(), 3); 30 | assert_eq!(bump.stats().allocated(), size_of::() * 3); 31 | let b: FixedBumpVec> = a.map_in_place(|s| { 32 | if i == panic_on { 33 | panic!("oh no"); 34 | } 35 | 36 | let value = format!("hello: {s}"); 37 | i += 1; 38 | Some(value) 39 | }); 40 | assert_eq!(b.capacity(), 3); 41 | assert_eq!(bump.stats().allocated(), size_of::>() * 3); 42 | dbg!(b); 43 | }); 44 | 45 | if panic_on == 0 { 46 | expect_no_panic(result); 47 | } else { 48 | assert_eq!(*result.unwrap_err().downcast::<&'static str>().unwrap(), "oh no"); 49 | } 50 | 51 | assert_eq!(bump.stats().allocated(), size_of::() * 3, "panic_on={panic_on}"); 52 | } 53 | } 54 | 55 | fn map_in_place_smaller_layout() { 56 | for panic_on in 0..4 { 57 | let bump: Bump = Bump::new(); 58 | 59 | let result = std::panic::catch_unwind(|| { 60 | let mut i = 1; 61 | let a = FixedBumpVec::::from_iter_exact_in([1, 2, 3].map(|i| i.to_string()), &bump); 62 | assert_eq!(a.capacity(), 3); 63 | assert_eq!(bump.stats().allocated(), size_of::() * 3); 64 | let b: FixedBumpVec> = a.map_in_place(|s| { 65 | if i == panic_on { 66 | panic!("oh no"); 67 | } 68 | 69 | let value = format!("hello: {s}"); 70 | i += 1; 71 | value.into() 72 | }); 73 | assert_eq!(b.capacity(), 4); 74 | assert_eq!(bump.stats().allocated(), size_of::() * 3); 75 | dbg!(b); 76 | }); 77 | 78 | if panic_on == 0 { 79 | expect_no_panic(result); 80 | } else { 81 | assert_eq!(*result.unwrap_err().downcast::<&'static str>().unwrap(), "oh no"); 82 | } 83 | 84 | assert_eq!(bump.stats().allocated(), size_of::() * 3, "panic_on={panic_on}"); 85 | } 86 | } 87 | 88 | fn map_in_place_to_zst() { 89 | for panic_on in 0..4 { 90 | let bump: Bump = Bump::new(); 91 | 92 | let result = std::panic::catch_unwind(|| { 93 | let mut i = 1; 94 | let a = FixedBumpVec::::from_iter_exact_in([1, 2, 3].map(|i| i.to_string()), &bump); 95 | assert_eq!(a.capacity(), 3); 96 | assert_eq!(bump.stats().allocated(), size_of::() * 3); 97 | let b: FixedBumpVec = a.map_in_place(|_| { 98 | if i == panic_on { 99 | panic!("oh no"); 100 | } 101 | 102 | i += 1; 103 | AlignedZst 104 | }); 105 | assert_eq!(b.capacity(), usize::MAX); 106 | assert_eq!(bump.stats().allocated(), size_of::() * 3); 107 | dbg!(b); 108 | }); 109 | 110 | if panic_on == 0 { 111 | expect_no_panic(result); 112 | } else { 113 | assert_eq!(*result.unwrap_err().downcast::<&'static str>().unwrap(), "oh no"); 114 | } 115 | 116 | assert_eq!(bump.stats().allocated(), size_of::() * 3, "panic_on={panic_on}"); 117 | } 118 | } 119 | 120 | fn map_in_place_from_zst_to_zst() { 121 | for panic_on in 0..4 { 122 | let bump: Bump = Bump::new(); 123 | 124 | let result = std::panic::catch_unwind(|| { 125 | let mut i = 1; 126 | let a = FixedBumpVec::<()>::from_iter_exact_in([(), (), ()], &bump); 127 | assert_eq!(a.capacity(), usize::MAX); 128 | assert_eq!(bump.stats().allocated(), 0); 129 | let b: FixedBumpVec = a.map_in_place(|()| { 130 | if i == panic_on { 131 | panic!("oh no"); 132 | } 133 | 134 | i += 1; 135 | AlignedZst 136 | }); 137 | assert_eq!(b.capacity(), usize::MAX); 138 | assert_eq!(bump.stats().allocated(), 0); 139 | dbg!(b); 140 | }); 141 | 142 | if panic_on == 0 { 143 | expect_no_panic(result); 144 | } else { 145 | assert_eq!(*result.unwrap_err().downcast::<&'static str>().unwrap(), "oh no"); 146 | } 147 | 148 | assert_eq!(bump.stats().allocated(), 0, "panic_on={panic_on}"); 149 | } 150 | } 151 | 152 | #[repr(align(1024))] 153 | #[derive(Debug)] 154 | struct AlignedZst; 155 | -------------------------------------------------------------------------------- /src/tests/fn_traits.rs: -------------------------------------------------------------------------------- 1 | use std::{panic::catch_unwind, string::String}; 2 | 3 | use crate::{Bump, BumpBox}; 4 | 5 | #[test] 6 | fn fn_once() { 7 | let bump: Bump = Bump::new(); 8 | 9 | let x: BumpBox = bump.alloc(|| {}); 10 | (x)(); 11 | 12 | // making sure the string is dropped 13 | let string = String::from("hello world"); 14 | let x: BumpBox = bump.alloc(move || drop(string)); 15 | (x)(); 16 | 17 | let string = String::from("hello world"); 18 | let x: BumpBox = bump.alloc(move || drop(string)); 19 | drop(x); 20 | 21 | #[allow(unused_variables)] 22 | catch_unwind(|| { 23 | let string = String::from("hello world"); 24 | let x: BumpBox = bump.alloc(move || { 25 | panic!("a"); 26 | #[allow(unreachable_code)] 27 | drop(string); 28 | }); 29 | (x)(); 30 | }) 31 | .unwrap_err(); 32 | } 33 | 34 | #[test] 35 | fn does_impl() { 36 | fn impls_fn_once(_: &T) {} 37 | fn impls_fn_mut(_: &T) {} 38 | fn impls_fn(_: &T) {} 39 | 40 | let bump: Bump = Bump::new(); 41 | 42 | let f = bump.alloc(|| {}); 43 | impls_fn_once(&f); 44 | impls_fn_mut(&f); 45 | impls_fn(&f); 46 | 47 | let mut string = String::from("hello"); 48 | let f = bump.alloc(|| string.push('x')); 49 | impls_fn_once(&f); 50 | impls_fn_mut(&f); 51 | 52 | let string = String::from("hello"); 53 | let f = bump.alloc(|| drop(string)); 54 | impls_fn_once(&f); 55 | 56 | let f: BumpBox = bump.alloc(|| {}); 57 | impls_fn_once(&f); 58 | impls_fn_mut(&f); 59 | impls_fn(&f); 60 | 61 | let mut string = String::from("hello"); 62 | let f: BumpBox = bump.alloc(|| string.push('x')); 63 | impls_fn_once(&f); 64 | impls_fn_mut(&f); 65 | 66 | let string = String::from("hello"); 67 | let f: BumpBox = bump.alloc(|| drop(string)); 68 | impls_fn_once(&f); 69 | } 70 | -------------------------------------------------------------------------------- /src/tests/grow_vec.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | string::{String, ToString}, 3 | vec::Vec, 4 | }; 5 | 6 | use crate::{Bump, BumpVec, MutBumpVec, MutBumpVecRev}; 7 | 8 | #[test] 9 | fn grow_vec() { 10 | let bump: Bump = Bump::new(); 11 | let mut vec = BumpVec::new_in(&bump); 12 | let mut numbers = number_strings(); 13 | 14 | vec.push(numbers.next().unwrap()); 15 | 16 | let ptr = vec.as_ptr(); 17 | 18 | while vec.as_ptr() == ptr { 19 | vec.push(numbers.next().unwrap()); 20 | } 21 | 22 | assert!(vec.iter().cloned().eq(number_strings().take(vec.len()))); 23 | } 24 | 25 | #[test] 26 | fn grow_mut_vec() { 27 | let mut bump: Bump = Bump::new(); 28 | let mut vec = MutBumpVec::new_in(&mut bump); 29 | let mut numbers = number_strings(); 30 | 31 | vec.push(numbers.next().unwrap()); 32 | 33 | let ptr = vec.as_ptr(); 34 | 35 | while vec.as_ptr() == ptr { 36 | vec.push(numbers.next().unwrap()); 37 | } 38 | 39 | assert!(vec.iter().cloned().eq(number_strings().take(vec.len()))); 40 | } 41 | 42 | #[test] 43 | fn grow_mut_vec_rev() { 44 | let mut bump: Bump = Bump::new(); 45 | let mut vec = MutBumpVecRev::new_in(&mut bump); 46 | let mut numbers = number_strings(); 47 | 48 | vec.push(numbers.next().unwrap()); 49 | 50 | let ptr = vec.as_ptr(); 51 | 52 | while vec.as_ptr() == ptr { 53 | vec.push(numbers.next().unwrap()); 54 | } 55 | 56 | assert!(vec 57 | .iter() 58 | .cloned() 59 | .eq(number_strings().take(vec.len()).collect::>().into_iter().rev())); 60 | } 61 | 62 | fn number_strings() -> impl Iterator { 63 | (0..).map(|i| i.to_string()) 64 | } 65 | -------------------------------------------------------------------------------- /src/tests/into_flattened.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | format, 3 | string::{String, ToString}, 4 | vec::Vec, 5 | }; 6 | 7 | use crate::{Bump, BumpVec, FixedBumpVec, MutBumpVec, MutBumpVecRev}; 8 | 9 | fn items() -> impl Iterator { 10 | // would use `array_chunks`, but it's not stable 11 | (1..) 12 | .map(|i| i.to_string()) 13 | .take(3 * 3) 14 | .collect::>() 15 | .chunks(3) 16 | .map(|s| s.to_vec().try_into().unwrap()) 17 | .collect::>() 18 | .into_iter() 19 | } 20 | 21 | #[test] 22 | fn boxed() { 23 | let bump: Bump = Bump::new(); 24 | let boxed = bump.alloc_iter(items()); 25 | assert_eq!(format!("{boxed:?}"), r#"[["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"]]"#); 26 | let boxed = boxed.into_flattened(); 27 | assert_eq!(format!("{boxed:?}"), r#"["1", "2", "3", "4", "5", "6", "7", "8", "9"]"#); 28 | } 29 | 30 | #[test] 31 | fn fixed_vec() { 32 | let bump: Bump = Bump::new(); 33 | let mut vec = FixedBumpVec::with_capacity_in(4, &bump); 34 | vec.extend(items()); 35 | assert_eq!(format!("{vec:?}"), r#"[["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"]]"#); 36 | let vec = vec.into_flattened(); 37 | assert_eq!(format!("{vec:?}"), r#"["1", "2", "3", "4", "5", "6", "7", "8", "9"]"#); 38 | assert_eq!(vec.capacity(), 12); 39 | } 40 | 41 | #[test] 42 | fn vec() { 43 | let bump: Bump = Bump::new(); 44 | let mut vec = BumpVec::with_capacity_in(4, &bump); 45 | vec.extend(items()); 46 | assert_eq!(format!("{vec:?}"), r#"[["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"]]"#); 47 | let vec = vec.into_flattened(); 48 | assert_eq!(format!("{vec:?}"), r#"["1", "2", "3", "4", "5", "6", "7", "8", "9"]"#); 49 | assert_eq!(vec.capacity(), 12); 50 | } 51 | 52 | #[test] 53 | fn mut_vec() { 54 | let mut bump: Bump = Bump::new(); 55 | let mut vec = MutBumpVec::with_capacity_in(4, &mut bump); 56 | let original_capacity = vec.capacity(); 57 | vec.extend(items()); 58 | assert_eq!(format!("{vec:?}"), r#"[["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"]]"#); 59 | let vec = vec.into_flattened(); 60 | assert_eq!(format!("{vec:?}"), r#"["1", "2", "3", "4", "5", "6", "7", "8", "9"]"#); 61 | assert_eq!(vec.capacity(), original_capacity * 3); 62 | } 63 | 64 | #[test] 65 | fn mut_vec_rev() { 66 | let mut bump: Bump = Bump::new(); 67 | let mut vec = MutBumpVecRev::with_capacity_in(4, &mut bump); 68 | let original_capacity = vec.capacity(); 69 | vec.extend(items()); 70 | assert_eq!(format!("{vec:?}"), r#"[["7", "8", "9"], ["4", "5", "6"], ["1", "2", "3"]]"#); 71 | let vec = vec.into_flattened(); 72 | assert_eq!(format!("{vec:?}"), r#"["7", "8", "9", "4", "5", "6", "1", "2", "3"]"#); 73 | assert_eq!(vec.capacity(), original_capacity * 3); 74 | } 75 | -------------------------------------------------------------------------------- /src/tests/io_write.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] 2 | 3 | use std::{ 4 | io::{ErrorKind, IoSlice, Write}, 5 | vec::Vec, 6 | }; 7 | 8 | use crate::{ 9 | alloc::{Allocator, Global}, 10 | Bump, BumpVec, FixedBumpVec, MutBumpVec, 11 | }; 12 | 13 | use super::limited_allocator::Limited; 14 | 15 | #[test] 16 | fn io_write_vec() { 17 | let bump: Bump> = Bump::new_in(Limited::new_in(512, Global)); 18 | let mut vec = BumpVec::new_in(&bump); 19 | assert!(matches!(vec.write(&[1, 2, 3]), Ok(3))); 20 | assert!(matches!( 21 | vec.write_vectored(&[IoSlice::new(&[4, 5]), IoSlice::new(&[6, 7, 8]),]), 22 | Ok(5) 23 | )); 24 | vec.write_all(&[9]).unwrap(); 25 | assert_eq!(vec, [1, 2, 3, 4, 5, 6, 7, 8, 9]); 26 | 27 | let too_much = (0..1024).map(|i| i as u8).collect::>(); 28 | assert_eq!(vec.write(&too_much).unwrap_err().kind(), ErrorKind::OutOfMemory); 29 | assert_eq!(vec.write_all(&too_much).unwrap_err().kind(), ErrorKind::OutOfMemory); 30 | assert_eq!( 31 | vec.write_vectored(&[IoSlice::new(&[10]), IoSlice::new(&too_much)]) 32 | .unwrap_err() 33 | .kind(), 34 | ErrorKind::OutOfMemory 35 | ); 36 | assert_eq!(vec, [1, 2, 3, 4, 5, 6, 7, 8, 9]); 37 | } 38 | 39 | #[test] 40 | fn io_write_mut_vec() { 41 | let mut bump: Bump> = Bump::new_in(Limited::new_in(512, Global)); 42 | let mut vec = MutBumpVec::new_in(&mut bump); 43 | assert!(matches!(vec.write(&[1, 2, 3]), Ok(3))); 44 | assert!(matches!( 45 | vec.write_vectored(&[IoSlice::new(&[4, 5]), IoSlice::new(&[6, 7, 8]),]), 46 | Ok(5) 47 | )); 48 | vec.write_all(&[9]).unwrap(); 49 | assert_eq!(vec, [1, 2, 3, 4, 5, 6, 7, 8, 9]); 50 | 51 | let too_much = (0..1024).map(|i| i as u8).collect::>(); 52 | assert_eq!(vec.write(&too_much).unwrap_err().kind(), ErrorKind::OutOfMemory); 53 | assert_eq!(vec.write_all(&too_much).unwrap_err().kind(), ErrorKind::OutOfMemory); 54 | assert_eq!( 55 | vec.write_vectored(&[IoSlice::new(&[10]), IoSlice::new(&too_much)]) 56 | .unwrap_err() 57 | .kind(), 58 | ErrorKind::OutOfMemory 59 | ); 60 | assert_eq!(vec, [1, 2, 3, 4, 5, 6, 7, 8, 9]); 61 | } 62 | 63 | #[test] 64 | fn io_write_fixed_vec() { 65 | let bump: Bump = Bump::new(); 66 | let mut vec = FixedBumpVec::with_capacity_in(1024, &bump); 67 | assert!(matches!(vec.write(&[1, 2, 3]), Ok(3))); 68 | assert!(matches!( 69 | vec.write_vectored(&[IoSlice::new(&[4, 5]), IoSlice::new(&[6, 7, 8]),]), 70 | Ok(5) 71 | )); 72 | vec.write_all(&[9]).unwrap(); 73 | assert_eq!(vec, [1, 2, 3, 4, 5, 6, 7, 8, 9]); 74 | 75 | let too_much = (0..1024).map(|i| i as u8).collect::>(); 76 | assert_eq!(vec.write(&too_much).unwrap_err().kind(), ErrorKind::OutOfMemory); 77 | assert_eq!(vec.write_all(&too_much).unwrap_err().kind(), ErrorKind::OutOfMemory); 78 | assert_eq!( 79 | vec.write_vectored(&[IoSlice::new(&[10]), IoSlice::new(&too_much)]) 80 | .unwrap_err() 81 | .kind(), 82 | ErrorKind::OutOfMemory 83 | ); 84 | assert_eq!(vec, [1, 2, 3, 4, 5, 6, 7, 8, 9]); 85 | } 86 | -------------------------------------------------------------------------------- /src/tests/limited_allocator.rs: -------------------------------------------------------------------------------- 1 | // copied from tests/limit_memory_usage.rs 2 | 3 | use std::{alloc::Layout, cell::Cell, ptr::NonNull}; 4 | 5 | use crate::alloc::{AllocError, Allocator, Global}; 6 | 7 | #[derive(Clone)] 8 | pub(crate) struct Limited { 9 | current: Cell, 10 | limit: usize, 11 | allocator: A, 12 | } 13 | 14 | impl Limited { 15 | pub(crate) fn new_in(limit: usize, allocator: A) -> Self { 16 | Self { 17 | current: Cell::new(0), 18 | limit, 19 | allocator, 20 | } 21 | } 22 | 23 | fn add(&self, size: usize) -> Result { 24 | let new = match self.current.get().checked_add(size) { 25 | Some(some) => some, 26 | None => return Err(AllocError), 27 | }; 28 | 29 | if new > self.limit { 30 | return Err(AllocError); 31 | } 32 | 33 | Ok(new) 34 | } 35 | 36 | fn sub(&self, size: usize) { 37 | self.current.set(self.current.get() - size); 38 | } 39 | } 40 | 41 | unsafe impl Allocator for Limited 42 | where 43 | A: Allocator, 44 | { 45 | fn allocate(&self, layout: Layout) -> Result, AllocError> { 46 | let new = self.add(layout.size())?; 47 | let ptr = self.allocator.allocate(layout)?; 48 | self.current.set(new); 49 | Ok(ptr) 50 | } 51 | 52 | unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { 53 | self.sub(layout.size()); 54 | self.allocator.deallocate(ptr, layout); 55 | } 56 | 57 | fn allocate_zeroed(&self, layout: Layout) -> Result, AllocError> { 58 | let new = self.add(layout.size())?; 59 | let ptr = self.allocator.allocate_zeroed(layout)?; 60 | self.current.set(new); 61 | Ok(ptr) 62 | } 63 | 64 | unsafe fn grow(&self, ptr: NonNull, old_layout: Layout, new_layout: Layout) -> Result, AllocError> { 65 | let new = self.add(new_layout.size() - old_layout.size())?; 66 | let ptr = self.allocator.grow(ptr, old_layout, new_layout)?; 67 | self.current.set(new); 68 | Ok(ptr) 69 | } 70 | 71 | unsafe fn grow_zeroed( 72 | &self, 73 | ptr: NonNull, 74 | old_layout: Layout, 75 | new_layout: Layout, 76 | ) -> Result, AllocError> { 77 | let new = self.add(new_layout.size() - old_layout.size())?; 78 | let ptr = self.allocator.grow_zeroed(ptr, old_layout, new_layout)?; 79 | self.current.set(new); 80 | Ok(ptr) 81 | } 82 | 83 | unsafe fn shrink(&self, ptr: NonNull, old_layout: Layout, new_layout: Layout) -> Result, AllocError> { 84 | let ptr = self.allocator.shrink(ptr, old_layout, new_layout)?; 85 | self.sub(old_layout.size() - new_layout.size()); 86 | Ok(ptr) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/tests/mut_bump_vec_doc.rs: -------------------------------------------------------------------------------- 1 | //! doc tests but for up and down 2 | 3 | use crate::{alloc::Global, mut_bump_vec, Bump, MutBumpVec}; 4 | 5 | use super::either_way; 6 | 7 | either_way! { 8 | new 9 | 10 | from_array 11 | 12 | from_elem 13 | 14 | extend_from_within_copy 15 | 16 | resize 17 | 18 | resize_with 19 | 20 | capacity 21 | 22 | insert 23 | 24 | remove 25 | 26 | swap_remove 27 | 28 | truncate 29 | } 30 | 31 | fn new() { 32 | let mut bump: Bump = Bump::new(); 33 | let vec: MutBumpVec = mut_bump_vec![in &mut bump]; 34 | assert!(vec.is_empty()); 35 | } 36 | 37 | fn from_array() { 38 | let mut bump: Bump = Bump::new(); 39 | let vec = mut_bump_vec![in &mut bump; 1, 2, 3]; 40 | assert_eq!(vec[0], 1); 41 | assert_eq!(vec[1], 2); 42 | assert_eq!(vec[2], 3); 43 | } 44 | 45 | fn from_elem() { 46 | let mut bump: Bump = Bump::new(); 47 | let vec = mut_bump_vec![in &mut bump; 1; 3]; 48 | assert_eq!(vec, [1, 1, 1]); 49 | } 50 | 51 | fn extend_from_within_copy() { 52 | let mut bump: Bump = Bump::new(); 53 | 54 | let mut vec = mut_bump_vec![in &mut bump; 0, 1, 2, 3, 4]; 55 | 56 | vec.extend_from_within_copy(2..); 57 | assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4]); 58 | 59 | vec.extend_from_within_copy(..2); 60 | assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4, 0, 1]); 61 | 62 | vec.extend_from_within_copy(4..8); 63 | assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4, 0, 1, 4, 2, 3, 4]); 64 | } 65 | 66 | fn resize() { 67 | let mut bump: Bump = Bump::new(); 68 | 69 | let mut vec = mut_bump_vec![in &mut bump; "hello"]; 70 | vec.resize(3, "world"); 71 | assert_eq!(vec, ["hello", "world", "world"]); 72 | drop(vec); 73 | 74 | let mut vec = mut_bump_vec![in &mut bump; 1, 2, 3, 4]; 75 | vec.resize(2, 0); 76 | assert_eq!(vec, [1, 2]); 77 | } 78 | 79 | fn resize_with() { 80 | let mut bump: Bump = Bump::new(); 81 | 82 | let mut vec = mut_bump_vec![in &mut bump; 1, 2, 3]; 83 | vec.resize_with(5, Default::default); 84 | assert_eq!(vec, [1, 2, 3, 0, 0]); 85 | drop(vec); 86 | 87 | let mut vec = mut_bump_vec![in &mut bump]; 88 | let mut p = 1; 89 | vec.resize_with(4, || { 90 | p *= 2; 91 | p 92 | }); 93 | assert_eq!(vec, [2, 4, 8, 16]); 94 | } 95 | 96 | fn capacity() { 97 | let mut bump: Bump = Bump::new(); 98 | 99 | let vec: MutBumpVec = MutBumpVec::with_capacity_in(2048, &mut bump); 100 | assert!(vec.capacity() >= 2048); 101 | } 102 | 103 | fn insert() { 104 | let mut bump: Bump = Bump::new(); 105 | 106 | let mut vec = mut_bump_vec![in &mut bump; 1, 2, 3]; 107 | vec.insert(1, 4); 108 | assert_eq!(vec, [1, 4, 2, 3]); 109 | vec.insert(4, 5); 110 | assert_eq!(vec, [1, 4, 2, 3, 5]); 111 | } 112 | 113 | fn remove() { 114 | let mut bump: Bump = Bump::new(); 115 | 116 | let mut v = mut_bump_vec![in &mut bump; 1, 2, 3]; 117 | assert_eq!(v.remove(1), 2); 118 | assert_eq!(v, [1, 3]); 119 | } 120 | 121 | fn swap_remove() { 122 | let mut bump: Bump = Bump::new(); 123 | 124 | let mut v = mut_bump_vec![in &mut bump; "foo", "bar", "baz", "qux"]; 125 | 126 | assert_eq!(v.swap_remove(1), "bar"); 127 | assert_eq!(v, ["foo", "qux", "baz"]); 128 | 129 | assert_eq!(v.swap_remove(0), "foo"); 130 | assert_eq!(v, ["baz", "qux"]); 131 | } 132 | 133 | fn truncate() { 134 | let mut bump: Bump = Bump::new(); 135 | 136 | { 137 | let mut vec = mut_bump_vec![in &mut bump; 1, 2, 3, 4, 5]; 138 | vec.truncate(2); 139 | assert_eq!(vec, [1, 2]); 140 | } 141 | 142 | { 143 | let mut vec = mut_bump_vec![in &mut bump; 1, 2, 3]; 144 | vec.truncate(8); 145 | assert_eq!(vec, [1, 2, 3]); 146 | } 147 | 148 | { 149 | let mut vec = mut_bump_vec![in &mut bump; 1, 2, 3]; 150 | vec.truncate(0); 151 | assert!(vec.is_empty()); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/tests/mut_bump_vec_rev_doc.rs: -------------------------------------------------------------------------------- 1 | //! doc tests but for up and down 2 | 3 | use crate::{alloc::Global, mut_bump_vec_rev, Bump, MutBumpVecRev}; 4 | 5 | use super::either_way; 6 | 7 | either_way! { 8 | new 9 | 10 | from_array 11 | 12 | from_elem 13 | 14 | extend_from_within_copy 15 | 16 | resize 17 | 18 | resize_with 19 | 20 | capacity 21 | 22 | insert 23 | 24 | remove 25 | 26 | swap_remove 27 | 28 | truncate 29 | } 30 | 31 | fn new() { 32 | let mut bump: Bump = Bump::new(); 33 | let vec: MutBumpVecRev = mut_bump_vec_rev![in &mut bump]; 34 | assert!(vec.is_empty()); 35 | } 36 | 37 | fn from_array() { 38 | let mut bump: Bump = Bump::new(); 39 | let vec = mut_bump_vec_rev![in &mut bump; 1, 2, 3]; 40 | assert_eq!(vec[0], 1); 41 | assert_eq!(vec[1], 2); 42 | assert_eq!(vec[2], 3); 43 | } 44 | 45 | fn from_elem() { 46 | let mut bump: Bump = Bump::new(); 47 | let vec = mut_bump_vec_rev![in &mut bump; 1; 3]; 48 | assert_eq!(vec, [1, 1, 1]); 49 | } 50 | 51 | fn extend_from_within_copy() { 52 | let mut bump: Bump = Bump::new(); 53 | 54 | let mut vec = mut_bump_vec_rev![in &mut bump; 0, 1, 2, 3, 4]; 55 | 56 | vec.extend_from_within_copy(2..); 57 | assert_eq!(vec, [2, 3, 4, 0, 1, 2, 3, 4]); 58 | 59 | vec.extend_from_within_copy(..2); 60 | assert_eq!(vec, [2, 3, 2, 3, 4, 0, 1, 2, 3, 4]); 61 | 62 | vec.extend_from_within_copy(4..8); 63 | assert_eq!(vec, [4, 0, 1, 2, 2, 3, 2, 3, 4, 0, 1, 2, 3, 4]); 64 | } 65 | 66 | fn resize() { 67 | let mut bump: Bump = Bump::new(); 68 | 69 | let mut vec = mut_bump_vec_rev![in &mut bump; "hello"]; 70 | vec.resize(3, "world"); 71 | assert_eq!(vec, ["world", "world", "hello"]); 72 | drop(vec); 73 | 74 | let mut vec = mut_bump_vec_rev![in &mut bump; 1, 2, 3, 4]; 75 | vec.resize(2, 0); 76 | assert_eq!(vec, [3, 4]); 77 | } 78 | 79 | fn resize_with() { 80 | let mut bump: Bump = Bump::new(); 81 | 82 | let mut vec = mut_bump_vec_rev![in &mut bump; 1, 2, 3]; 83 | vec.resize_with(5, Default::default); 84 | assert_eq!(vec, [0, 0, 1, 2, 3]); 85 | drop(vec); 86 | 87 | let mut vec = mut_bump_vec_rev![in &mut bump]; 88 | let mut p = 1; 89 | vec.resize_with(4, || { 90 | p *= 2; 91 | p 92 | }); 93 | assert_eq!(vec, [16, 8, 4, 2]); 94 | } 95 | 96 | fn capacity() { 97 | let mut bump: Bump = Bump::new(); 98 | 99 | let vec: MutBumpVecRev = MutBumpVecRev::with_capacity_in(2048, &mut bump); 100 | assert!(vec.capacity() >= 2048); 101 | } 102 | 103 | fn insert() { 104 | let mut bump: Bump = Bump::new(); 105 | 106 | let mut vec = mut_bump_vec_rev![in &mut bump; 1, 2, 3]; 107 | vec.insert(1, 4); 108 | assert_eq!(vec, [1, 4, 2, 3]); 109 | vec.insert(4, 5); 110 | assert_eq!(vec, [1, 4, 2, 3, 5]); 111 | } 112 | 113 | fn remove() { 114 | let mut bump: Bump = Bump::new(); 115 | 116 | let mut v = mut_bump_vec_rev![in &mut bump; 1, 2, 3]; 117 | assert_eq!(v.remove(1), 2); 118 | assert_eq!(v, [1, 3]); 119 | } 120 | 121 | fn swap_remove() { 122 | let mut bump: Bump = Bump::new(); 123 | 124 | let mut v = mut_bump_vec_rev![in &mut bump; "foo", "bar", "baz", "qux"]; 125 | 126 | assert_eq!(v.swap_remove(1), "bar"); 127 | assert_eq!(v, ["foo", "baz", "qux"]); 128 | 129 | assert_eq!(v.swap_remove(0), "foo"); 130 | assert_eq!(v, ["baz", "qux"]); 131 | } 132 | 133 | fn truncate() { 134 | let mut bump: Bump = Bump::new(); 135 | 136 | { 137 | let mut vec = mut_bump_vec_rev![in &mut bump; 1, 2, 3, 4, 5]; 138 | vec.truncate(2); 139 | assert_eq!(vec, [4, 5]); 140 | } 141 | 142 | { 143 | let mut vec = mut_bump_vec_rev![in &mut bump; 1, 2, 3]; 144 | vec.truncate(8); 145 | assert_eq!(vec, [1, 2, 3]); 146 | } 147 | 148 | { 149 | let mut vec = mut_bump_vec_rev![in &mut bump; 1, 2, 3]; 150 | vec.truncate(0); 151 | assert!(vec.is_empty()); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/tests/mut_collections_do_not_waste_space.rs: -------------------------------------------------------------------------------- 1 | use std::iter; 2 | 3 | use crate::{alloc::Global, Bump, MutBumpVec, MutBumpVecRev}; 4 | 5 | use super::either_way; 6 | 7 | fn vec() { 8 | let mut bump: Bump = Bump::with_size(512); 9 | assert_eq!(bump.stats().size(), 512 - size_of::<[usize; 2]>()); 10 | 11 | for size in [0, 100, 200, 300, 400] { 12 | bump.reset(); 13 | 14 | let mut vec: MutBumpVec = MutBumpVec::new_in(&mut bump); 15 | vec.extend(iter::repeat(0).take(size)); 16 | assert_eq!(vec.allocator_stats().allocated(), 0); // `Mut*` allocations don't bump the pointer 17 | _ = vec.into_slice(); 18 | assert_eq!(bump.stats().allocated(), size); 19 | } 20 | } 21 | 22 | fn vec_rev() { 23 | let mut bump: Bump = Bump::with_size(512); 24 | assert_eq!(bump.stats().size(), 512 - size_of::<[usize; 2]>()); 25 | 26 | for size in [0, 100, 200, 300, 400] { 27 | bump.reset(); 28 | 29 | let mut vec: MutBumpVecRev = MutBumpVecRev::new_in(&mut bump); 30 | vec.extend(iter::repeat(0).take(size)); 31 | assert_eq!(vec.allocator_stats().allocated(), 0); // `Mut*` allocations don't bump the pointer 32 | _ = vec.into_slice(); 33 | assert_eq!(bump.stats().allocated(), size); 34 | } 35 | } 36 | 37 | either_way! { 38 | vec 39 | vec_rev 40 | } 41 | -------------------------------------------------------------------------------- /src/tests/pool.rs: -------------------------------------------------------------------------------- 1 | use std::vec::Vec; 2 | 3 | use rayon::iter::{IntoParallelIterator, ParallelIterator}; 4 | 5 | use crate::{alloc::Global, BumpPool}; 6 | 7 | use super::either_way; 8 | 9 | either_way! { 10 | rayon 11 | 12 | scope 13 | } 14 | 15 | fn rayon() { 16 | if cfg!(miri) { 17 | // rayon violates strict-provenance :( 18 | return; 19 | } 20 | 21 | let mut pool = BumpPool::::new(); 22 | 23 | let ints: Vec<&mut usize> = (0..1000usize) 24 | .into_par_iter() 25 | .map_init( 26 | || pool.get(), 27 | |bump, i| { 28 | // do some expensive work 29 | bump.alloc(i).into_mut() 30 | }, 31 | ) 32 | .collect(); 33 | 34 | assert!((0..1000usize).eq(ints.iter().map(|i| **i))); 35 | 36 | pool.reset(); 37 | } 38 | 39 | fn scope() { 40 | let pool = BumpPool::::new(); 41 | let (sender, receiver) = std::sync::mpsc::sync_channel(10); 42 | 43 | std::thread::scope(|s| { 44 | s.spawn(|| { 45 | let bump = pool.get(); 46 | let string = bump.alloc_str("Hello").into_ref(); 47 | sender.send(string).unwrap(); 48 | drop(sender); 49 | }); 50 | 51 | s.spawn(|| { 52 | for string in receiver { 53 | assert_eq!(string, "Hello"); 54 | } 55 | }); 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /src/tests/serde.rs: -------------------------------------------------------------------------------- 1 | use std::vec; 2 | 3 | use ::serde::{de::DeserializeSeed, Serialize}; 4 | 5 | use crate::{bump_format, bump_vec, FixedBumpString, FixedBumpVec}; 6 | 7 | use super::*; 8 | 9 | fn assert_same(a: &A, b: &B) { 10 | let a_json = serde_json::to_string(a).unwrap(); 11 | let b_json = serde_json::to_string(b).unwrap(); 12 | assert_eq!(a_json, b_json); 13 | } 14 | 15 | #[test] 16 | fn ser() { 17 | let mut bump: Bump = Bump::new(); 18 | 19 | { 20 | let a = bump.alloc_str("Hello, world!"); 21 | let b = "Hello, world!"; 22 | assert_same(&a, &b); 23 | } 24 | 25 | { 26 | let mut a = FixedBumpVec::with_capacity_in(5, &bump); 27 | a.extend_from_slice_copy(&[1, 2, 3]); 28 | let b = vec![1, 2, 3]; 29 | assert_same(&a, &b); 30 | } 31 | 32 | { 33 | let a = bump_vec![in ≎ 1, 2, 3]; 34 | let b = vec![1, 2, 3]; 35 | assert_same(&a, &b); 36 | } 37 | 38 | { 39 | let a = mut_bump_vec![in &mut bump; 1, 2, 3]; 40 | let b = vec![1, 2, 3]; 41 | assert_same(&a, &b); 42 | } 43 | 44 | { 45 | let a = mut_bump_vec_rev![in &mut bump; 1, 2, 3]; 46 | let b = vec![1, 2, 3]; 47 | assert_same(&a, &b); 48 | } 49 | 50 | { 51 | let a = bump_format!(in &bump, "Hello, world!"); 52 | let b = "Hello, world!"; 53 | assert_same(&a, &b); 54 | } 55 | 56 | { 57 | let a = mut_bump_format!(in &mut bump, "Hello, world!"); 58 | let b = "Hello, world!"; 59 | assert_same(&a, &b); 60 | } 61 | } 62 | 63 | fn roundtrip(src: &T, dst: &mut T) 64 | where 65 | T: Serialize + PartialEq + Debug, 66 | for<'de, 'a> &'a mut T: DeserializeSeed<'de>, 67 | { 68 | let json = serde_json::to_string(src).unwrap(); 69 | let mut deserializer = serde_json::Deserializer::from_str(&json); 70 | dst.deserialize(&mut deserializer).unwrap(); 71 | 72 | assert_eq!(*src, *dst); 73 | } 74 | 75 | #[test] 76 | fn de() { 77 | let mut bump_src: Bump = Bump::new(); 78 | let mut bump_dst: Bump = Bump::new(); 79 | 80 | { 81 | let mut src = FixedBumpVec::with_capacity_in(3, &bump_src); 82 | src.extend_from_slice_copy(&[1, 2, 3]); 83 | let mut dst = FixedBumpVec::with_capacity_in(3, &bump_dst); 84 | roundtrip(&src, &mut dst); 85 | } 86 | 87 | { 88 | let src = bump_vec![in &bump_src; 1, 2, 3]; 89 | let mut dst = bump_vec![in &bump_dst]; 90 | roundtrip(&src, &mut dst); 91 | } 92 | 93 | { 94 | let src = mut_bump_vec![in &mut bump_src; 1, 2, 3]; 95 | let mut dst = mut_bump_vec![in &mut bump_dst]; 96 | roundtrip(&src, &mut dst); 97 | } 98 | 99 | { 100 | let src = mut_bump_vec_rev![in &mut bump_src; 1, 2, 3]; 101 | let mut dst: MutBumpVecRev = mut_bump_vec_rev![in &mut bump_dst]; 102 | 103 | let json = serde_json::to_string(&src).unwrap(); 104 | let mut deserializer = serde_json::Deserializer::from_str(&json); 105 | dst.deserialize(&mut deserializer).unwrap(); 106 | dst.reverse(); 107 | 108 | assert_eq!(*src, *dst); 109 | } 110 | 111 | { 112 | let mut src = FixedBumpString::with_capacity_in(15, &bump_src); 113 | src.push_str("Hello, World!"); 114 | let mut dst = FixedBumpString::with_capacity_in(15, &bump_dst); 115 | roundtrip(&src, &mut dst); 116 | } 117 | 118 | { 119 | let src = bump_format!(in &bump_src, "Hello, World!"); 120 | let mut dst = bump_format!(in &bump_dst); 121 | roundtrip(&src, &mut dst); 122 | } 123 | 124 | { 125 | let src = mut_bump_format!(in &mut bump_src, "Hello, World!"); 126 | let mut dst = mut_bump_format!(in &mut bump_dst); 127 | roundtrip(&src, &mut dst); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/tests/test_wrap.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::Cell, convert::Infallible, fmt, thread_local}; 2 | 3 | thread_local! { 4 | static DROPS: Cell = const { Cell::new(0) }; 5 | static CLONES: Cell = const { Cell::new(0) }; 6 | static DEFAULTS: Cell = const { Cell::new(0) }; 7 | } 8 | 9 | #[repr(transparent)] 10 | pub(crate) struct TestWrap(pub T); 11 | 12 | impl fmt::Debug for TestWrap { 13 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 14 | fmt::Debug::fmt(&self.0, f) 15 | } 16 | } 17 | 18 | impl PartialEq for TestWrap { 19 | fn eq(&self, other: &Self) -> bool { 20 | self.0.eq(&other.0) 21 | } 22 | } 23 | 24 | impl Default for TestWrap { 25 | fn default() -> Self { 26 | DEFAULTS.set(DEFAULTS.get() + 1); 27 | TestWrap(T::default()) 28 | } 29 | } 30 | 31 | impl Clone for TestWrap { 32 | fn clone(&self) -> Self { 33 | CLONES.set(CLONES.get() + 1); 34 | Self(self.0.clone()) 35 | } 36 | } 37 | 38 | impl Drop for TestWrap { 39 | fn drop(&mut self) { 40 | DROPS.set(DROPS.get() + 1); 41 | } 42 | } 43 | 44 | impl TestWrap { 45 | pub(crate) fn peel_slice(this: &[TestWrap]) -> &[T] { 46 | unsafe { &*(this as *const [TestWrap] as *const [T]) } 47 | } 48 | } 49 | 50 | impl TestWrap { 51 | pub(crate) fn expect() -> TestZstExpect { 52 | TestZstExpect::default() 53 | } 54 | 55 | #[expect(dead_code)] 56 | pub(crate) fn current_defaults() -> usize { 57 | DEFAULTS.get() 58 | } 59 | 60 | #[expect(dead_code)] 61 | pub(crate) fn current_clones() -> usize { 62 | CLONES.get() 63 | } 64 | 65 | #[expect(dead_code)] 66 | pub(crate) fn current_drops() -> usize { 67 | DROPS.get() 68 | } 69 | } 70 | 71 | #[derive(Default)] 72 | pub(crate) struct TestZstExpect { 73 | drops: usize, 74 | clones: usize, 75 | defaults: usize, 76 | } 77 | 78 | impl TestZstExpect { 79 | pub(crate) fn drops(mut self, amount: usize) -> Self { 80 | self.drops = amount; 81 | self 82 | } 83 | 84 | pub(crate) fn clones(mut self, amount: usize) -> Self { 85 | self.clones = amount; 86 | self 87 | } 88 | 89 | pub(crate) fn defaults(mut self, amount: usize) -> Self { 90 | self.defaults = amount; 91 | self 92 | } 93 | 94 | pub(crate) fn run(self, f: impl FnOnce() -> R) -> R { 95 | DROPS.set(0); 96 | CLONES.set(0); 97 | DEFAULTS.set(0); 98 | 99 | let result = f(); 100 | 101 | macro_rules! expected { 102 | ($expected:ident, $actual:ident, $what:literal) => { 103 | if self.$expected != $actual.get() { 104 | panic!( 105 | "expected {expected} {what}, got {actual}", 106 | expected = self.$expected, 107 | actual = $actual.get(), 108 | what = $what, 109 | ) 110 | } 111 | }; 112 | } 113 | 114 | expected!(drops, DROPS, "drops"); 115 | expected!(clones, CLONES, "clones"); 116 | expected!(defaults, DEFAULTS, "defaults"); 117 | 118 | result 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/tests/unaligned_collection.rs: -------------------------------------------------------------------------------- 1 | use std::dbg; 2 | 3 | use super::*; 4 | 5 | either_way! { 6 | bump_vec 7 | mut_bump_vec 8 | mut_bump_vec_rev 9 | } 10 | 11 | fn bump_vec() { 12 | let bump = Bump::::new(); 13 | 14 | bump.alloc(8u8); 15 | 16 | let mut vec = BumpVec::new_in(&bump); 17 | vec.push(32u32); 18 | 19 | let slice = vec.into_slice(); 20 | dbg!(slice); 21 | } 22 | 23 | fn mut_bump_vec() { 24 | let mut bump = Bump::::new(); 25 | 26 | bump.alloc(8u8); 27 | 28 | let mut vec = MutBumpVec::new_in(&mut bump); 29 | vec.push(32u32); 30 | 31 | let slice = vec.into_slice(); 32 | dbg!(slice); 33 | } 34 | 35 | fn mut_bump_vec_rev() { 36 | let mut bump = Bump::::new(); 37 | 38 | bump.alloc(8u8); 39 | 40 | let mut vec = MutBumpVecRev::new_in(&mut bump); 41 | vec.push(32u32); 42 | 43 | let slice = vec.into_slice(); 44 | dbg!(slice); 45 | } 46 | -------------------------------------------------------------------------------- /src/tests/unallocated.rs: -------------------------------------------------------------------------------- 1 | use crate::alloc::Global; 2 | 3 | type Bump = crate::Bump; 4 | 5 | #[test] 6 | fn allocated() { 7 | let bump: Bump = Bump::new(); 8 | drop(bump); 9 | } 10 | 11 | #[test] 12 | fn unallocated() { 13 | let bump: Bump = Bump::unallocated(); 14 | drop(bump); 15 | } 16 | 17 | #[test] 18 | fn allocated_by_usage() { 19 | let bump: Bump = Bump::unallocated(); 20 | bump.alloc_str("Hello, World!"); 21 | drop(bump); 22 | } 23 | 24 | #[test] 25 | fn guaranteed_allocated() { 26 | let bump: Bump = Bump::unallocated(); 27 | let bump = bump.guaranteed_allocated(); 28 | assert!(bump.stats().size() > 0); 29 | drop(bump); 30 | } 31 | 32 | #[test] 33 | fn allocated_reserve_bytes() { 34 | let bump: Bump = Bump::new(); 35 | bump.reserve_bytes(1024); 36 | assert!(bump.stats().capacity() >= 1024); 37 | drop(bump); 38 | } 39 | 40 | #[test] 41 | fn unallocated_reserve_bytes() { 42 | let bump: Bump = Bump::unallocated(); 43 | bump.reserve_bytes(1024); 44 | assert!(bump.stats().capacity() >= 1024); 45 | drop(bump); 46 | } 47 | -------------------------------------------------------------------------------- /src/tests/vec.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "allocator-api2-02")] 2 | 3 | use allocator_api2_02::{boxed::Box, vec::Vec}; 4 | 5 | use crate::{alloc::Global, Bump}; 6 | 7 | use super::either_way; 8 | 9 | either_way! { 10 | grow 11 | 12 | shrink 13 | 14 | shrinknt 15 | } 16 | 17 | fn grow() { 18 | let mut bump: Bump = Bump::new(); 19 | 20 | let mut vec = Vec::::new_in(&bump); 21 | assert_eq!(bump.stats().allocated(), 0); 22 | 23 | vec.reserve_exact(1); 24 | assert_eq!(bump.stats().allocated(), 4); 25 | 26 | vec.reserve_exact(2); 27 | assert_eq!(bump.stats().allocated(), 8); 28 | 29 | bump.alloc_uninit::(); 30 | assert_eq!(bump.stats().allocated(), 9); 31 | 32 | vec.reserve_exact(3); 33 | assert_eq!(bump.stats().allocated(), 12 + 3 * 4); 34 | 35 | drop(vec); 36 | assert_eq!(bump.stats().allocated(), 12); 37 | 38 | bump.reset(); 39 | assert_eq!(bump.stats().allocated(), 0); 40 | } 41 | 42 | fn shrink() { 43 | let bump: Bump = Bump::new(); 44 | 45 | let mut vec = Vec::::new_in(&bump); 46 | assert_eq!(bump.stats().allocated(), 0); 47 | 48 | vec.reserve_exact(2); 49 | assert_eq!(bump.stats().allocated(), 8); 50 | 51 | vec.shrink_to(1); 52 | assert_eq!(bump.stats().allocated(), 4); 53 | 54 | let boxed: Box<_, _> = bump.alloc_uninit::().into_box(&bump); 55 | assert_eq!(bump.stats().allocated(), 5); 56 | 57 | drop(boxed); 58 | assert_eq!(bump.stats().allocated(), 4); 59 | 60 | vec.shrink_to_fit(); 61 | assert_eq!(bump.stats().allocated(), 0); 62 | } 63 | 64 | fn shrinknt() { 65 | let bump: Bump = Bump::new(); 66 | 67 | let mut vec = Vec::::new_in(&bump); 68 | assert_eq!(bump.stats().allocated(), 0); 69 | 70 | vec.reserve_exact(2); 71 | assert_eq!(bump.stats().allocated(), 8); 72 | 73 | bump.alloc_uninit::(); 74 | assert_eq!(bump.stats().allocated(), 9); 75 | 76 | vec.shrink_to(1); 77 | assert_eq!(bump.stats().allocated(), 9); 78 | 79 | drop(vec); 80 | assert_eq!(bump.stats().allocated(), 9); 81 | } 82 | -------------------------------------------------------------------------------- /src/without_dealloc.rs: -------------------------------------------------------------------------------- 1 | use core::{alloc::Layout, ptr::NonNull}; 2 | 3 | use crate::{ 4 | alloc::{AllocError, Allocator}, 5 | polyfill::non_null, 6 | BumpAllocator, 7 | }; 8 | 9 | /// Wraps a bump allocator and does nothing on [`deallocate`](Allocator::deallocate). 10 | /// 11 | /// This type only implements [`Allocator`] for wrapped types that implement [`BumpAllocator`], so you don't accidentally leak memory. 12 | #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 13 | pub struct WithoutDealloc(pub A); 14 | 15 | impl WithoutDealloc { 16 | /// Wraps `self` in [`WithoutShrink`] so that [`shrink`] becomes a no-op. 17 | /// 18 | /// [`shrink`]: Allocator::shrink 19 | pub fn without_shrink(self) -> WithoutShrink { 20 | WithoutShrink(self) 21 | } 22 | } 23 | 24 | unsafe impl Allocator for WithoutDealloc { 25 | #[inline(always)] 26 | fn allocate(&self, layout: Layout) -> Result, AllocError> { 27 | self.0.allocate(layout) 28 | } 29 | 30 | #[inline(always)] 31 | fn allocate_zeroed(&self, layout: Layout) -> Result, AllocError> { 32 | self.0.allocate_zeroed(layout) 33 | } 34 | 35 | #[inline(always)] 36 | unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { 37 | let _ = (ptr, layout); 38 | } 39 | 40 | #[inline(always)] 41 | unsafe fn grow(&self, ptr: NonNull, old_layout: Layout, new_layout: Layout) -> Result, AllocError> { 42 | self.0.grow(ptr, old_layout, new_layout) 43 | } 44 | 45 | #[inline(always)] 46 | unsafe fn grow_zeroed( 47 | &self, 48 | ptr: NonNull, 49 | old_layout: Layout, 50 | new_layout: Layout, 51 | ) -> Result, AllocError> { 52 | self.0.grow_zeroed(ptr, old_layout, new_layout) 53 | } 54 | 55 | #[inline(always)] 56 | unsafe fn shrink(&self, ptr: NonNull, old_layout: Layout, new_layout: Layout) -> Result, AllocError> { 57 | self.0.shrink(ptr, old_layout, new_layout) 58 | } 59 | } 60 | 61 | /// Wraps a bump allocator and does nothing on [`shrink`](Allocator::shrink). 62 | /// 63 | /// This type only implements [`Allocator`] for wrapped types that implement [`BumpAllocator`], so you don't accidentally leak memory. 64 | #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 65 | pub struct WithoutShrink(pub A); 66 | 67 | impl WithoutShrink { 68 | /// Wraps `self` in [`WithoutDealloc`] so that [`deallocate`] becomes a no-op. 69 | /// 70 | /// [`deallocate`]: Allocator::deallocate 71 | pub fn without_dealloc(self) -> WithoutDealloc { 72 | WithoutDealloc(self) 73 | } 74 | } 75 | 76 | unsafe impl Allocator for WithoutShrink { 77 | #[inline(always)] 78 | fn allocate(&self, layout: Layout) -> Result, AllocError> { 79 | self.0.allocate(layout) 80 | } 81 | 82 | #[inline(always)] 83 | fn allocate_zeroed(&self, layout: Layout) -> Result, AllocError> { 84 | self.0.allocate_zeroed(layout) 85 | } 86 | 87 | #[inline(always)] 88 | unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { 89 | self.0.deallocate(ptr, layout); 90 | } 91 | 92 | #[inline(always)] 93 | unsafe fn grow(&self, ptr: NonNull, old_layout: Layout, new_layout: Layout) -> Result, AllocError> { 94 | self.0.grow(ptr, old_layout, new_layout) 95 | } 96 | 97 | #[inline(always)] 98 | unsafe fn grow_zeroed( 99 | &self, 100 | ptr: NonNull, 101 | old_layout: Layout, 102 | new_layout: Layout, 103 | ) -> Result, AllocError> { 104 | self.0.grow_zeroed(ptr, old_layout, new_layout) 105 | } 106 | 107 | #[inline(always)] 108 | unsafe fn shrink(&self, ptr: NonNull, old_layout: Layout, new_layout: Layout) -> Result, AllocError> { 109 | #[cold] 110 | #[inline(never)] 111 | unsafe fn shrink_unfit( 112 | this: &WithoutShrink, 113 | ptr: NonNull, 114 | old_layout: Layout, 115 | new_layout: Layout, 116 | ) -> Result, AllocError> { 117 | let new_ptr = this.0.allocate(new_layout)?.cast::(); 118 | non_null::copy_nonoverlapping(ptr, new_ptr, old_layout.size()); 119 | Ok(non_null::slice_from_raw_parts(new_ptr, new_layout.size())) 120 | } 121 | 122 | if non_null::is_aligned_to(ptr, new_layout.align()) { 123 | Ok(non_null::slice_from_raw_parts(ptr, new_layout.size())) 124 | } else { 125 | // expected to virtually never occur 126 | shrink_unfit(self, ptr, old_layout, new_layout) 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /tests/compile_fail.rs: -------------------------------------------------------------------------------- 1 | #[cfg_attr(miri, ignore)] 2 | #[test] 3 | fn mustnt_compile() { 4 | let t = trybuild::TestCases::new(); 5 | t.compile_fail("tests/compile_fail/*.rs"); 6 | } 7 | -------------------------------------------------------------------------------- /tests/compile_fail/escape_closure.rs: -------------------------------------------------------------------------------- 1 | use bump_scope::Bump; 2 | 3 | fn escape_closure(bump: &mut Bump) { 4 | let mut escapee = None; 5 | 6 | bump.scoped(|scope| { 7 | escapee = Some(scope.alloc("escape?")); 8 | }); 9 | 10 | dbg!(escapee); 11 | } 12 | 13 | fn main() {} 14 | -------------------------------------------------------------------------------- /tests/compile_fail/escape_closure.stderr: -------------------------------------------------------------------------------- 1 | error[E0521]: borrowed data escapes outside of closure 2 | --> tests/compile_fail/escape_closure.rs:7:9 3 | | 4 | 4 | let mut escapee = None; 5 | | ----------- `escapee` declared here, outside of the closure body 6 | 5 | 7 | 6 | bump.scoped(|scope| { 8 | | ----- `scope` is a reference that is only valid in the closure body 9 | 7 | escapee = Some(scope.alloc("escape?")); 10 | | ^^^^^^^ `scope` escapes the closure body here 11 | -------------------------------------------------------------------------------- /tests/compile_fail/escape_closure_scope.rs: -------------------------------------------------------------------------------- 1 | use bump_scope::BumpScope; 2 | 3 | fn escape_closure(mut bump: BumpScope) { 4 | let mut escapee = None; 5 | 6 | bump.scoped(|scope| { 7 | escapee = Some(scope.alloc("escape?")); 8 | }); 9 | 10 | dbg!(escapee); 11 | } 12 | 13 | fn main() {} 14 | -------------------------------------------------------------------------------- /tests/compile_fail/escape_closure_scope.stderr: -------------------------------------------------------------------------------- 1 | error[E0521]: borrowed data escapes outside of closure 2 | --> tests/compile_fail/escape_closure_scope.rs:7:9 3 | | 4 | 4 | let mut escapee = None; 5 | | ----------- `escapee` declared here, outside of the closure body 6 | 5 | 7 | 6 | bump.scoped(|scope| { 8 | | ----- `scope` is a reference that is only valid in the closure body 9 | 7 | escapee = Some(scope.alloc("escape?")); 10 | | ^^^^^^^ `scope` escapes the closure body here 11 | -------------------------------------------------------------------------------- /tests/compile_fail/escape_guard.rs: -------------------------------------------------------------------------------- 1 | use bump_scope::Bump; 2 | 3 | #[allow(unused_assignments)] 4 | fn escape_closure(bump: &mut Bump) { 5 | let mut escapee = None; 6 | 7 | { 8 | let mut guard = bump.scope_guard(); 9 | let scope = guard.scope(); 10 | 11 | escapee = Some(scope.alloc("escape?")); 12 | } 13 | 14 | dbg!(escapee); 15 | } 16 | 17 | fn main() {} 18 | -------------------------------------------------------------------------------- /tests/compile_fail/escape_guard.stderr: -------------------------------------------------------------------------------- 1 | error[E0597]: `guard` does not live long enough 2 | --> tests/compile_fail/escape_guard.rs:9:21 3 | | 4 | 8 | let mut guard = bump.scope_guard(); 5 | | --------- binding `guard` declared here 6 | 9 | let scope = guard.scope(); 7 | | ^^^^^ borrowed value does not live long enough 8 | ... 9 | 12 | } 10 | | - `guard` dropped here while still borrowed 11 | 13 | 12 | 14 | dbg!(escapee); 13 | | ------- borrow later used here 14 | -------------------------------------------------------------------------------- /tests/compile_fail/escape_guard_scope.rs: -------------------------------------------------------------------------------- 1 | use bump_scope::BumpScope; 2 | 3 | #[allow(unused_assignments)] 4 | fn escape_closure(mut bump: BumpScope) { 5 | let mut escapee = None; 6 | 7 | { 8 | let mut guard = bump.scope_guard(); 9 | let scope = guard.scope(); 10 | 11 | escapee = Some(scope.alloc("escape?")); 12 | } 13 | 14 | dbg!(escapee); 15 | } 16 | 17 | fn main() {} 18 | -------------------------------------------------------------------------------- /tests/compile_fail/escape_guard_scope.stderr: -------------------------------------------------------------------------------- 1 | error[E0597]: `guard` does not live long enough 2 | --> tests/compile_fail/escape_guard_scope.rs:9:21 3 | | 4 | 8 | let mut guard = bump.scope_guard(); 5 | | --------- binding `guard` declared here 6 | 9 | let scope = guard.scope(); 7 | | ^^^^^ borrowed value does not live long enough 8 | ... 9 | 12 | } 10 | | - `guard` dropped here while still borrowed 11 | 13 | 12 | 14 | dbg!(escapee); 13 | | ------- borrow later used here 14 | -------------------------------------------------------------------------------- /tests/compile_fail/multiple_scopes.rs: -------------------------------------------------------------------------------- 1 | use bump_scope::{Bump, BumpScope}; 2 | 3 | #[allow(unused_assignments)] 4 | fn multiple_scopes(bump: &mut Bump) { 5 | fn use_scope(scope: BumpScope) -> &str { 6 | scope.alloc_str("foo").into_ref() 7 | } 8 | 9 | let mut guard = bump.scope_guard(); 10 | 11 | let a = use_scope(guard.scope()); 12 | let b = use_scope(guard.scope()); 13 | 14 | dbg!(a); 15 | dbg!(b); 16 | } 17 | 18 | fn main() {} 19 | -------------------------------------------------------------------------------- /tests/compile_fail/multiple_scopes.stderr: -------------------------------------------------------------------------------- 1 | error[E0499]: cannot borrow `guard` as mutable more than once at a time 2 | --> tests/compile_fail/multiple_scopes.rs:12:23 3 | | 4 | 11 | let a = use_scope(guard.scope()); 5 | | ----- first mutable borrow occurs here 6 | 12 | let b = use_scope(guard.scope()); 7 | | ^^^^^ second mutable borrow occurs here 8 | 13 | 9 | 14 | dbg!(a); 10 | | - first borrow later used here 11 | -------------------------------------------------------------------------------- /tests/compile_fail/unsize_to_different_lifetime.rs: -------------------------------------------------------------------------------- 1 | use bump_scope::{unsize_bump_box, BumpBox}; 2 | 3 | fn evil_unsize(boxed: BumpBox<[i32; 3]>) -> BumpBox<'static, [i32]> { 4 | unsize_bump_box!(boxed) 5 | } 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /tests/compile_fail/unsize_to_different_lifetime.stderr: -------------------------------------------------------------------------------- 1 | error: lifetime may not live long enough 2 | --> tests/compile_fail/unsize_to_different_lifetime.rs:4:5 3 | | 4 | 3 | fn evil_unsize(boxed: BumpBox<[i32; 3]>) -> BumpBox<'static, [i32]> { 5 | | ----- has type `BumpBox<'1, [i32; 3]>` 6 | 4 | unsize_bump_box!(boxed) 7 | | ^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` 8 | | 9 | = note: this error originates in the macro `unsize_bump_box` (in Nightly builds, run with -Z macro-backtrace for more info) 10 | -------------------------------------------------------------------------------- /tests/limit_memory_usage.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(feature = "nightly-allocator-api", feature(allocator_api))] 2 | #![cfg(feature = "alloc")] 3 | 4 | use std::{alloc::Layout, cell::Cell, ptr::NonNull}; 5 | 6 | use bump_scope::{ 7 | alloc::{AllocError, Allocator, Global}, 8 | Bump, 9 | }; 10 | 11 | struct Limited { 12 | current: Cell, 13 | limit: usize, 14 | allocator: A, 15 | } 16 | 17 | impl Limited { 18 | pub fn new_in(limit: usize, allocator: A) -> Self { 19 | Self { 20 | current: Cell::new(0), 21 | limit, 22 | allocator, 23 | } 24 | } 25 | 26 | fn add(&self, size: usize) -> Result { 27 | let new = match self.current.get().checked_add(size) { 28 | Some(some) => some, 29 | None => return Err(AllocError), 30 | }; 31 | 32 | if new > self.limit { 33 | return Err(AllocError); 34 | } 35 | 36 | Ok(new) 37 | } 38 | 39 | fn sub(&self, size: usize) { 40 | self.current.set(self.current.get() - size); 41 | } 42 | } 43 | 44 | unsafe impl Allocator for Limited 45 | where 46 | A: Allocator, 47 | { 48 | fn allocate(&self, layout: Layout) -> Result, AllocError> { 49 | let new = self.add(layout.size())?; 50 | let ptr = self.allocator.allocate(layout)?; 51 | self.current.set(new); 52 | Ok(ptr) 53 | } 54 | 55 | unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { 56 | self.sub(layout.size()); 57 | self.allocator.deallocate(ptr, layout); 58 | } 59 | 60 | fn allocate_zeroed(&self, layout: Layout) -> Result, AllocError> { 61 | let new = self.add(layout.size())?; 62 | let ptr = self.allocator.allocate_zeroed(layout)?; 63 | self.current.set(new); 64 | Ok(ptr) 65 | } 66 | 67 | unsafe fn grow(&self, ptr: NonNull, old_layout: Layout, new_layout: Layout) -> Result, AllocError> { 68 | let new = self.add(new_layout.size() - old_layout.size())?; 69 | let ptr = self.allocator.grow(ptr, old_layout, new_layout)?; 70 | self.current.set(new); 71 | Ok(ptr) 72 | } 73 | 74 | unsafe fn grow_zeroed( 75 | &self, 76 | ptr: NonNull, 77 | old_layout: Layout, 78 | new_layout: Layout, 79 | ) -> Result, AllocError> { 80 | let new = self.add(new_layout.size() - old_layout.size())?; 81 | let ptr = self.allocator.grow_zeroed(ptr, old_layout, new_layout)?; 82 | self.current.set(new); 83 | Ok(ptr) 84 | } 85 | 86 | unsafe fn shrink(&self, ptr: NonNull, old_layout: Layout, new_layout: Layout) -> Result, AllocError> { 87 | let ptr = self.allocator.shrink(ptr, old_layout, new_layout)?; 88 | self.sub(old_layout.size() - new_layout.size()); 89 | Ok(ptr) 90 | } 91 | } 92 | 93 | #[test] 94 | fn main() { 95 | let allocator = Limited::new_in(1024, Global); 96 | 97 | let bump = Bump::<_, 1, true>::with_size_in(1024, &allocator); 98 | 99 | // limit is reached, trying to allocate any new chunk will fail 100 | // note that a bump `with_size` of 1024 results in a capacity of (1024 - SOME_HEADER_DATA_SIZE) 101 | bump.try_reserve_bytes(1024).unwrap_err(); 102 | } 103 | -------------------------------------------------------------------------------- /tests/static_memory.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "panic-on-alloc")] 2 | #![cfg_attr(feature = "nightly-allocator-api", feature(allocator_api))] 3 | 4 | use core::{ 5 | alloc::Layout, 6 | cell::Cell, 7 | mem::{self, MaybeUninit}, 8 | ptr::{self, NonNull}, 9 | }; 10 | use std::sync::{Mutex, PoisonError}; 11 | 12 | use bump_scope::{ 13 | alloc::{AllocError, Allocator}, 14 | Bump, 15 | }; 16 | 17 | #[repr(C, align(16))] 18 | struct StaticAllocator { 19 | memory: Cell<[MaybeUninit; SIZE]>, 20 | taken: Cell, 21 | } 22 | 23 | impl StaticAllocator { 24 | const fn new() -> Self { 25 | Self { 26 | memory: Cell::new([MaybeUninit::uninit(); SIZE]), 27 | taken: Cell::new(false), 28 | } 29 | } 30 | 31 | fn check_align(&self, align: usize) -> Result<(), AllocError> { 32 | if align <= mem::align_of::() { 33 | Ok(()) 34 | } else { 35 | Err(AllocError) 36 | } 37 | } 38 | 39 | fn check_layout(&self, layout: Layout) -> Result<(), AllocError> { 40 | self.check_align(layout.align())?; 41 | 42 | if layout.size() <= SIZE { 43 | Ok(()) 44 | } else { 45 | Err(AllocError) 46 | } 47 | } 48 | 49 | fn memory_ptr(&self) -> NonNull<[u8]> { 50 | let ptr = self.memory.as_ptr().cast::(); 51 | let slice = ptr::slice_from_raw_parts_mut(ptr, SIZE); 52 | NonNull::new(slice).unwrap() 53 | } 54 | } 55 | 56 | unsafe impl Allocator for StaticAllocator { 57 | fn allocate(&self, layout: Layout) -> Result, AllocError> { 58 | if self.taken.get() { 59 | return Err(AllocError); 60 | } 61 | 62 | self.check_layout(layout)?; 63 | self.taken.set(true); 64 | Ok(self.memory_ptr()) 65 | } 66 | 67 | unsafe fn deallocate(&self, _ptr: NonNull, _layout: Layout) { 68 | self.taken.set(false); 69 | } 70 | 71 | fn allocate_zeroed(&self, layout: Layout) -> Result, AllocError> { 72 | let ptr = self.allocate(layout)?; 73 | // SAFETY: `alloc` returns a valid memory block 74 | unsafe { ptr.cast::().as_ptr().write_bytes(0, ptr.len()) } 75 | Ok(ptr) 76 | } 77 | 78 | unsafe fn grow(&self, _ptr: NonNull, _old_layout: Layout, new_layout: Layout) -> Result, AllocError> { 79 | self.check_layout(new_layout)?; 80 | Ok(self.memory_ptr()) 81 | } 82 | 83 | unsafe fn grow_zeroed( 84 | &self, 85 | ptr: NonNull, 86 | old_layout: Layout, 87 | new_layout: Layout, 88 | ) -> Result, AllocError> { 89 | self.check_layout(new_layout)?; 90 | 91 | let zero_ptr = ptr.as_ptr().add(old_layout.size()); 92 | let zero_len = new_layout.size() - old_layout.size(); 93 | 94 | unsafe { 95 | zero_ptr.write_bytes(0, zero_len); 96 | } 97 | 98 | Ok(self.memory_ptr()) 99 | } 100 | 101 | unsafe fn shrink( 102 | &self, 103 | _ptr: NonNull, 104 | _old_layout: Layout, 105 | new_layout: Layout, 106 | ) -> Result, AllocError> { 107 | self.check_align(new_layout.align())?; 108 | Ok(self.memory_ptr()) 109 | } 110 | } 111 | 112 | fn on_stack() { 113 | let memory = StaticAllocator::<1024>::new(); 114 | 115 | let bump = Bump::<_, 1, true>::new_in(&memory); 116 | assert_eq!(bump.stats().size(), 1024); 117 | 118 | let str = bump.alloc_str("It works!"); 119 | println!("{str}"); 120 | 121 | bump.try_alloc_layout(Layout::new::<[u8; 2048]>()).unwrap_err(); 122 | } 123 | 124 | fn on_static() { 125 | static MEMORY: Mutex> = Mutex::new(StaticAllocator::new()); 126 | let guard = MEMORY.lock().unwrap_or_else(PoisonError::into_inner); 127 | let memory = &*guard; 128 | 129 | let bump = Bump::<_, 1, true>::new_in(memory); 130 | assert_eq!(bump.stats().size(), 1024); 131 | 132 | let str = bump.alloc_str("It works!"); 133 | println!("{str}"); 134 | 135 | bump.try_alloc_layout(Layout::new::<[u8; 2048]>()).unwrap_err(); 136 | } 137 | 138 | #[test] 139 | fn main() { 140 | on_stack(); 141 | on_static(); 142 | } 143 | -------------------------------------------------------------------------------- /tests/thread_local.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "alloc")] 2 | #![cfg_attr(feature = "nightly-allocator-api", feature(allocator_api))] 3 | 4 | use bump_scope::alloc::Global; 5 | 6 | type Bump = bump_scope::Bump; 7 | 8 | thread_local! { 9 | static BUMP: Bump = const { Bump::unallocated() }; 10 | } 11 | 12 | #[test] 13 | fn main() { 14 | BUMP.with(|bump| { 15 | let hello = bump.alloc_str("hello"); 16 | assert_eq!(hello, "hello"); 17 | }); 18 | } 19 | --------------------------------------------------------------------------------