├── .cargo
└── config.toml
├── .github
├── FUNDING.yml
├── actions-rs
│ └── grcov.yml
└── workflows
│ ├── docs.yml
│ ├── grcov.yml
│ ├── lint.yml
│ ├── publish.yml
│ └── test.yml
├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── Cargo.toml
├── LICENSE-APACHE
├── LICENSE-MIT
├── README.md
├── benches
└── region.rs
├── rustfmt.toml
└── src
├── affix.rs
├── callback_ref.rs
├── chunk.rs
├── fallback.rs
├── helper.rs
├── lib.rs
├── macros.rs
├── null.rs
├── proxy.rs
├── region
├── mod.rs
└── raw.rs
├── segregate.rs
└── stats.rs
/.cargo/config.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | rustflags = ["-C", "target-cpu=native"]
3 |
4 | [profile.release]
5 | lto = true
6 | # codegen-units = 1
7 | panic = "abort"
8 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: TimDiekmann
2 | ko_fi: timdiekmann
3 |
--------------------------------------------------------------------------------
/.github/actions-rs/grcov.yml:
--------------------------------------------------------------------------------
1 | branch: true
2 | ignore-not-existing: true
3 | llvm: true
4 | filter: covered
5 | output-type: lcov
6 | output-path: ./lcov.info
7 | source-dir: .
8 | ignore:
9 | - "/*"
10 | - "C:/*"
11 | - "../*"
12 | excl-line: "#\\[derive\\("
13 | excl-start: "mod tests \\{"
14 | excl-br-line: "#\\[derive\\("
15 | excl-br-start: "mod tests \\{"
16 |
--------------------------------------------------------------------------------
/.github/workflows/docs.yml:
--------------------------------------------------------------------------------
1 | name: Documentation
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | docs:
10 | name: Documentation
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Checkout source code
14 | uses: actions/checkout@v2
15 | with:
16 | persist-credentials: false
17 |
18 | - name: Install Rust
19 | uses: actions-rs/toolchain@v1
20 | with:
21 | profile: minimal
22 | toolchain: nightly
23 | override: true
24 |
25 | - name: Build documentation
26 | uses: actions-rs/cargo@v1
27 | with:
28 | command: doc
29 | args: --verbose --no-deps --all-features
30 |
31 | - name: Finalize documentation
32 | run: |
33 | CRATE_NAME=$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]' | cut -f2 -d"/")
34 | echo "" > target/doc/index.html
35 | touch target/doc/.nojekyll
36 |
37 | - name: Upload as artifact
38 | uses: actions/upload-artifact@v2
39 | with:
40 | name: Documentation
41 | path: target/doc
42 |
43 | - name: Deploy
44 | uses: JamesIves/github-pages-deploy-action@releases/v3
45 | with:
46 | ACCESS_TOKEN: ${{ secrets.GH_PAT }}
47 | BRANCH: gh-pages
48 | FOLDER: target/doc
49 |
--------------------------------------------------------------------------------
/.github/workflows/grcov.yml:
--------------------------------------------------------------------------------
1 | name: Coverage
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 |
9 | jobs:
10 | grcov:
11 | name: Coverage
12 | runs-on: ${{ matrix.os }}
13 | strategy:
14 | matrix:
15 | os:
16 | - ubuntu-latest
17 | # We don't have branches on OS currently
18 | # - macOS-latest
19 | # - windows-latest
20 | toolchain:
21 | - nightly
22 | cargo_flags:
23 | - "--all-features"
24 | steps:
25 | - name: Checkout source code
26 | uses: actions/checkout@v2
27 |
28 | - name: Install Rust
29 | uses: actions-rs/toolchain@v1
30 | with:
31 | profile: minimal
32 | toolchain: ${{ matrix.toolchain }}
33 | override: true
34 |
35 | - name: Install grcov
36 | uses: actions-rs/install@v0.1
37 | with:
38 | crate: grcov
39 | version: latest
40 | use-tool-cache: true
41 |
42 | - name: Test
43 | uses: actions-rs/cargo@v1
44 | with:
45 | command: test
46 | args: --all --no-fail-fast ${{ matrix.cargo_flags }}
47 | env:
48 | CARGO_INCREMENTAL: "0"
49 | RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort -Cdebug-assertions=off'
50 | RUSTDOCFLAGS: '-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort -Cdebug-assertions=off'
51 |
52 | - name: Generate coverage data
53 | id: grcov
54 | # uses: actions-rs/grcov@v0.1
55 | run: |
56 | grcov target/debug/ \
57 | --branch \
58 | --llvm \
59 | --source-dir . \
60 | --output-path lcov.info \
61 | --ignore='/**' \
62 | --ignore='C:/**' \
63 | --ignore='../**' \
64 | --ignore-not-existing \
65 | --excl-line "#\\[derive\\(" \
66 | --excl-br-line "#\\[derive\\(" \
67 | --excl-start "#\\[cfg\\(test\\)\\]" \
68 | --excl-br-start "#\\[cfg\\(test\\)\\]" \
69 | --commit-sha ${{ github.sha }} \
70 | --service-job-id ${{ github.job }} \
71 | --service-name "GitHub Actions" \
72 | --service-number ${{ github.run_id }}
73 |
74 | - name: Upload coverage as artifact
75 | uses: actions/upload-artifact@v2
76 | with:
77 | name: lcov.info
78 | # path: ${{ steps.grcov.outputs.report }}
79 | path: lcov.info
80 |
81 | - name: Upload coverage to codecov.io
82 | uses: codecov/codecov-action@v1
83 | with:
84 | # file: ${{ steps.grcov.outputs.report }}
85 | file: lcov.info
86 | fail_ci_if_error: true
87 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 |
9 | defaults:
10 | run:
11 | shell: bash
12 |
13 | env:
14 | CLIPPY_PARAMS: -W clippy::all -W clippy::pedantic -W clippy::nursery -W clippy::cargo
15 |
16 | jobs:
17 | rustfmt:
18 | name: rustfmt
19 | runs-on: ubuntu-latest
20 | steps:
21 | - name: Checkout source code
22 | uses: actions/checkout@v2
23 |
24 | - name: Install Rust
25 | uses: actions-rs/toolchain@v1
26 | with:
27 | profile: minimal
28 | toolchain: nightly
29 | override: true
30 | components: rustfmt
31 |
32 | - name: Run rustfmt
33 | uses: actions-rs/cargo@v1
34 | with:
35 | command: fmt
36 | args: --all -- --check --verbose
37 |
38 | # tomlfmt:
39 | # name: tomlfmt
40 | # runs-on: ubuntu-latest
41 | # steps:
42 | # - name: Checkout source code
43 | # uses: actions/checkout@master
44 |
45 | # - name: Install Rust
46 | # uses: actions-rs/toolchain@v1
47 | # with:
48 | # profile: minimal
49 | # toolchain: nightly
50 | # override: true
51 |
52 | # - name: Install tomlfmt
53 | # uses: actions-rs/install@v0.1
54 | # with:
55 | # crate: cargo-tomlfmt
56 | # version: latest
57 | # use-tool-cache: true
58 |
59 | # - name: Run Tomlfmt
60 | # uses: actions-rs/cargo@v1
61 | # with:
62 | # command: tomlfmt
63 | # args: --dryrun
64 |
65 | clippy:
66 | name: clippy
67 | runs-on: ubuntu-latest
68 | steps:
69 | - name: Checkout source code
70 | uses: actions/checkout@master
71 |
72 | - name: Install Rust
73 | uses: actions-rs/toolchain@v1
74 | with:
75 | profile: minimal
76 | toolchain: nightly
77 | override: true
78 | components: clippy
79 |
80 | - name: Run clippy
81 | uses: actions-rs/clippy-check@v1
82 | with:
83 | token: ${{ secrets.GITHUB_TOKEN }}
84 | args: --all-features -- ${{ env.CLIPPY_PARAMS }}
85 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish
2 |
3 | on:
4 | push:
5 | tags:
6 | - "*"
7 |
8 | jobs:
9 | test:
10 | name: Publish
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Checkout source code
14 | uses: actions/checkout@v2
15 |
16 | - name: Install Rust
17 | uses: actions-rs/toolchain@v1
18 | with:
19 | profile: minimal
20 | toolchain: nightly
21 | override: true
22 |
23 | - name: Publish
24 | uses: actions-rs/cargo@v1
25 | with:
26 | command: publish
27 | args: --verbose --all-features --token ${{ secrets.CARGO_TOKEN }}
28 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 |
9 | jobs:
10 | test:
11 | name: Test
12 | runs-on: ${{ matrix.os }}
13 | strategy:
14 | matrix:
15 | os:
16 | - ubuntu-latest
17 | - windows-latest
18 | - macOS-latest
19 | toolchain:
20 | - nightly
21 | cargo_flags:
22 | - "--no-default-features"
23 | - "--all-features"
24 | steps:
25 | - name: Checkout source code
26 | uses: actions/checkout@v2
27 |
28 | - name: Install Rust
29 | uses: actions-rs/toolchain@v1
30 | with:
31 | profile: minimal
32 | toolchain: ${{ matrix.toolchain }}
33 | override: true
34 |
35 | - name: Build
36 | uses: actions-rs/cargo@v1
37 | with:
38 | command: build
39 | args: --all ${{ matrix.cargo_flags }}
40 |
41 | - name: Test
42 | uses: actions-rs/cargo@v1
43 | with:
44 | command: test
45 | args: --all ${{ matrix.cargo_flags }}
46 |
47 |
48 | miri:
49 | name: miri
50 | runs-on: ubuntu-latest
51 | steps:
52 | - name: Checkout source code
53 | uses: actions/checkout@master
54 |
55 | - name: Install Rust
56 | uses: actions-rs/toolchain@v1
57 | with:
58 | profile: minimal
59 | toolchain: nightly
60 | override: true
61 | components: miri
62 |
63 | - name: Setup miri
64 | run: cargo miri setup
65 |
66 | - name: Run miri
67 | run: cargo miri test --all-features
68 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | Cargo.lock
3 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [v0.5](https://docs.rs/alloc-compose/0.5)
2 |
3 | - Add `ReallocInPlace` trait
4 | - Fix a bug in `Segregate` returning the wrong size
5 |
6 | **Breaking Changes:**
7 | - Update to nightly-2020-08-10, which uses `NonNull<[u8]>` and bans `InPlace` reallocations
8 | - Add `AllocAll` trait and move some methods from `Region` into that trait
9 | - Change `Region` to require `[MaybeUninit]` rather than `[u8]`
10 | - Remove `MemoryMarker`
11 |
12 | ## [v0.4](https://docs.rs/alloc-compose/0.4)
13 |
14 | - **Breaking Change** Using unified naming scheme
15 | - **Breaking Change** Change `CallbackRef` to listen on `before_` and `after_` events
16 | - Greatly improve documentation of `Affix`
17 |
18 | ### [v0.3.1](https://docs.rs/alloc-compose/0.3)
19 |
20 | - Add more documentation
21 | - Add more tests
22 |
23 | ## [v0.3.0](https://docs.rs/alloc-compose/0.3)
24 |
25 | - **Breaking Change** Use `const_generics` in `SegregateAlloc`
26 | - Add `AffixAlloc`, `ChunkAlloc`, and `MemoryMarker`
27 | - Add more tests
28 |
29 | ## [v0.2](https://docs.rs/alloc-compose/0.2)
30 |
31 | - **Breaking Change** Use `core::alloc` instead of `alloc_wg`
32 | - Add `Region`, `CallbackRef`, `Proxy`, and `stats`
33 | - Add more tests
34 |
35 | ## [v0.1](https://docs.rs/alloc-compose/0.1)
36 |
37 | - Initial release: `Owns`, `NullAlloc`, `FallbackAlloc`, and `SegregateAlloc`
38 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by submitting an issue. All complaints will be reviewed and investigated
59 | and will result in a response that is deemed necessary and appropriate to the
60 | circumstances. The project team is obligated to maintain confidentiality with
61 | regard to the reporter of an incident. Further details of specific enforcement
62 | policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "alloc-compose"
3 | version = "0.5.0"
4 | authors = ["Tim Diekmann "]
5 | edition = "2018"
6 | description = "Composable allocator structures for plugging together more powerful allocators"
7 | repository = "https://github.com/TimDiekmann/alloc-compose"
8 | documentation = "https://docs.rs/alloc-compose"
9 | readme = "README.md"
10 | keywords = ["alloc"]
11 | categories = ["no-std"]
12 | license = "MIT OR Apache-2.0"
13 | exclude = [".github/**"]
14 |
15 | [features]
16 | alloc = []
17 | default = ["alloc"]
18 | intrinsics = []
19 |
20 | [dev-dependencies]
21 | criterion = { version = "0.3", features = ["real_blackbox"] }
22 |
23 | [[bench]]
24 | name = "region"
25 | harness = false
26 |
--------------------------------------------------------------------------------
/LICENSE-APACHE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/LICENSE-MIT:
--------------------------------------------------------------------------------
1 | Permission is hereby granted, free of charge, to any
2 | person obtaining a copy of this software and associated
3 | documentation files (the "Software"), to deal in the
4 | Software without restriction, including without
5 | limitation the rights to use, copy, modify, merge,
6 | publish, distribute, sublicense, and/or sell copies of
7 | the Software, and to permit persons to whom the Software
8 | is furnished to do so, subject to the following
9 | conditions:
10 |
11 | The above copyright notice and this permission notice
12 | shall be included in all copies or substantial portions
13 | of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 | DEALINGS IN THE SOFTWARE.
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/TimDiekmann/alloc-compose/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster)
2 | [](https://codecov.io/gh/TimDiekmann/alloc-compose)
3 | [](https://timdiekmann.github.io/alloc-compose/alloc_compose/index.html)
4 | [](https://docs.rs/alloc-compose)
5 | [](https://crates.io/crates/alloc-compose)
6 | 
7 |
8 | ---
9 |
10 | Important note
11 | --------------
12 |
13 | Due to some changes to `AllocRef` it was hard to keep this crate updated. I'll readd the functionality from v0.5.0 from time to time. Most things have to be refactored as `AllocRef`s reallocation methods now takes two layouts.
14 |
15 | The most interesting part as of now is probably `Region` and its variants.
16 | In future version, composable blocks like `AffixAllocator` or `Proxy` will be added.
17 |
18 | ---
19 |
20 | Composable allocator structures for plugging together more powerful allocators.
21 |
22 | `alloc-compose` relies on [`AllocRef`] as allocator trait. Until `AllocRef` has been stabilized, this crate requires a nightly compiler.
23 |
24 |
25 | The design of composable allocators is inspired by
26 | [`std::allocator` Is to Allocation what `std::vector` Is to Vexation][vid] by Andrei
27 | Alexandrescu and the [Phobos Standard Library][phobos] of the [D Programming Language][D].
28 |
29 | [`AllocRef`]: https://doc.rust-lang.org/nightly/core/alloc/trait.AllocRef.html
30 | [vid]: https://www.youtube.com/watch?v=LIb3L4vKZ7U
31 | [phobos]: https://github.com/dlang/phobos
32 | [D]: https://dlang.org/
33 |
34 | License
35 | -------
36 |
37 | Alloc-Compose is distributed under the terms of both the MIT license and the Apache License (Version 2.0).
38 |
39 | See [LICENSE-APACHE](https://github.com/TimDiekmann/alloc-compose/blob/master/LICENSE-APACHE) and [LICENSE-MIT](https://github.com/TimDiekmann/alloc-compose/blob/master/LICENSE-MIT) for details.
40 |
--------------------------------------------------------------------------------
/benches/region.rs:
--------------------------------------------------------------------------------
1 | #![feature(allocator_api)]
2 |
3 | use alloc_compose::{region::*, AllocateAll};
4 | use core::{
5 | alloc::{AllocRef, Layout},
6 | mem::MaybeUninit,
7 | };
8 |
9 | use criterion::{black_box, criterion_group, criterion_main, Bencher, Criterion};
10 |
11 | fn regions(c: &mut Criterion) {
12 | let mut group = c.benchmark_group("region");
13 | let mut data = [MaybeUninit::uninit(); 1024 * 1024];
14 |
15 | #[inline]
16 | fn run(region: impl AllocRef + AllocateAll, b: &mut Bencher) {
17 | b.iter(|| {
18 | for _ in 0..16 {
19 | region.alloc(black_box(Layout::new::<[u8; 16]>())).unwrap();
20 | }
21 | region.deallocate_all();
22 | })
23 | }
24 |
25 | group.bench_function("Region", |b| run(Region::new(&mut data), b));
26 | group.bench_function("SharedRegion", |b| run(SharedRegion::new(&mut data), b));
27 | group.bench_function("IntrusiveRegion", |b| {
28 | run(IntrusiveRegion::new(&mut data), b)
29 | });
30 | group.bench_function("&Region", |b| run(&Region::new(&mut data), b));
31 |
32 | group.finish();
33 | }
34 |
35 | criterion_group! {
36 | name = benches;
37 | config = Criterion::default().sample_size(1000).measurement_time(std::time::Duration::from_secs(3));
38 | targets = regions
39 | }
40 | criterion_main!(benches);
41 |
--------------------------------------------------------------------------------
/rustfmt.toml:
--------------------------------------------------------------------------------
1 | # General
2 | edition = "2018"
3 | version = "Two"
4 | unstable_features = true
5 |
6 | # Line breaking
7 | newline_style = "Unix"
8 |
9 | # Comments
10 | format_code_in_doc_comments = true
11 |
12 | # Formatting
13 | imports_layout = "HorizontalVertical"
14 | format_strings = true
15 | merge_derives = true
16 | format_macro_matchers = true
17 | overflow_delimited_expr = true
18 |
19 | # Reordering
20 | merge_imports = true
21 |
22 | # Shorthands
23 | use_try_shorthand = true
24 | use_field_init_shorthand = true
25 | condense_wildcard_suffixes = true
26 |
--------------------------------------------------------------------------------
/src/affix.rs:
--------------------------------------------------------------------------------
1 | use crate::{helper::AllocInit, AllocAll, ReallocInPlace};
2 | use core::{
3 | alloc::{AllocErr, AllocRef, Layout},
4 | fmt,
5 | marker::PhantomData,
6 | mem::{self, MaybeUninit},
7 | ptr::{self, NonNull},
8 | };
9 |
10 | /// An allocator that requests some extra memory from the parent allocator for storing
11 | /// a prefix and/or a suffix.
12 | ///
13 | /// The alignment of the memory block is the maximum of the alignment of `Prefix` and the requested
14 | /// alignment. This may introduce an unused padding between `Prefix` and the returned memory.
15 | ///
16 | /// To get a pointer to the prefix or the suffix, the [`prefix()`] and [`suffix()`] may be called.
17 | ///
18 | /// [`prefix()`]: Self::prefix
19 | /// [`suffix()`]: Self::suffix
20 | ///
21 | /// # Performance
22 | ///
23 | /// Generally it's faster to calculate the pointer to the prefix than the pointer to the suffix, as
24 | /// the extended layout of `Prefix` and the requested memory is needed in order to calculate the
25 | /// `Suffix` pointer. Additionally, in most cases it's recommended to use a prefix over a suffix for
26 | /// a more efficient use of memory. However, small prefixes blunt the alignment so if a large
27 | /// alignment with a small affix is needed, suffixes may be the better option.
28 | ///
29 | /// For layouts known at compile time the compiler is able to optimize away almost all calculations.
30 | ///
31 | /// # Examples
32 | ///
33 | /// `Prefix` is `12` bytes in size and has an alignment requirement of `4` bytes. `Suffix` is `16`
34 | /// bytes in size, the requested layout requires `28` bytes, both with an alignment of `8` bytes.
35 | /// The parent allocator returns memory blocks of `128` bytes to demonstrate the behavior on
36 | /// overallocating.
37 | /// ```
38 | /// #![feature(allocator_api)]
39 | ///
40 | /// use alloc_compose::{Affix, Chunk};
41 | /// use std::alloc::{Layout, System};
42 | ///
43 | /// type Prefix = [u32; 3];
44 | /// # assert_eq!(core::mem::size_of::(), 12);
45 | /// # assert_eq!(core::mem::align_of::(), 4);
46 | /// type Suffix = [u64; 2];
47 | /// # assert_eq!(core::mem::size_of::(), 16);
48 | /// # assert_eq!(core::mem::align_of::(), 8);
49 | /// type Alloc = Affix, Prefix, Suffix>;
50 | ///
51 | /// let layout = Layout::from_size_align(28, 8)?;
52 | /// # Ok::<(), core::alloc::LayoutErr>(())
53 | /// ```
54 | ///
55 | /// The memory layout differs depending on `Prefix` and `Suffix`:
56 | ///
57 | /// ```
58 | /// #![feature(slice_ptr_get, slice_ptr_len)]
59 | /// # #![feature(allocator_api)]
60 | /// # use alloc_compose::{Affix, Chunk};
61 | /// # use std::alloc::{Layout, System};
62 | ///
63 | /// use core::alloc::AllocRef;
64 | /// # type Prefix = [u32; 3];
65 | /// # type Suffix = [u64; 2];
66 | /// # type Alloc = Affix, Prefix, Suffix>;
67 | /// # let layout = Layout::from_size_align(28, 8).unwrap();
68 | ///
69 | /// let mut my_alloc = Alloc::default();
70 | ///
71 | /// // 0 12 16 44 48 64 128
72 | /// // ╞═ Prefix ══╡ ╞════ requested memory ═════╡ ╞═══ Suffix ════╡ │
73 | /// // ┢┳┳┳┳┳┳┳┳┳┳┳╅┬┬┬╆┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳╈┳┳┳╈┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳╅┬┬╌╌╌╌┬┬┤
74 | /// // ┡┻┻┻┻┻┻┻┻┻┻┻┹┴┴┴╄┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻╇┻┻┻╇┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┹┴┴╌╌╌╌┴┴┘
75 | /// // │ ├┄┄┄┄┄┄ layout.size() ┄┄┄┄┄┄┘ │
76 | /// // │ ├┄┄┄┄┄┄┄┄ memory.len() ┄┄┄┄┄┄┄┄┄┤
77 | /// // └→ prefix() └→ memory └→ suffix()
78 | /// let memory = my_alloc.alloc(layout)?;
79 | ///
80 | /// assert_eq!(memory.len(), 32);
81 | /// unsafe {
82 | /// assert_eq!(
83 | /// Alloc::prefix(memory.as_non_null_ptr(), layout).cast().as_ptr(),
84 | /// memory.as_mut_ptr().sub(16)
85 | /// );
86 | /// assert_eq!(
87 | /// Alloc::suffix(memory.as_non_null_ptr(), layout).cast().as_ptr(),
88 | /// memory.as_mut_ptr().add(32)
89 | /// );
90 | /// }
91 | /// # Ok::<(), core::alloc::AllocErr>(())
92 | /// ```
93 | ///
94 | /// The memory between `Prefix` and the requested memory is unused. If there is a padding between
95 | /// the requested memory and the suffix, this can be used as extra memory for the allocation. The
96 | /// memory after `Suffix` is also unused as `Suffix` is typed. This results in `68` bytes unused
97 | /// memory.
98 | ///
99 | /// If `Suffix` is a zero-sized type, the space after the requested memory block can be used:
100 | ///
101 | /// ```
102 | /// # #![feature(allocator_api, slice_ptr_get, slice_ptr_len)]
103 | /// # use alloc_compose::{Affix, Chunk};
104 | /// # use std::alloc::{Layout, System, AllocRef};
105 | /// use core::ptr::NonNull;
106 | /// # type Prefix = [u32; 3];
107 | ///
108 | /// // For convenience, the suffix can be ommitted
109 | /// type Alloc = Affix, Prefix>;
110 | /// # let layout = Layout::from_size_align(28, 8).unwrap();
111 | ///
112 | /// let mut my_alloc = Alloc::default();
113 | ///
114 | /// // 0 12 16 44 48 64 128
115 | /// // ╞═ Prefix ══╡ ╞════ requested memory ═════╡ │ │ │
116 | /// // ┢┳┳┳┳┳┳┳┳┳┳┳╅┬┬┬╆┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳╈┳┳┳╈┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳╈┳┳╍╍╍╍┳┳┪
117 | /// // ┡┻┻┻┻┻┻┻┻┻┻┻┹┴┴┴╄┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻╇┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻╍╍╍╍┻┻┩
118 | /// // │ ├┄┄┄┄┄┄ layout.size() ┄┄┄┄┄┄┘ │
119 | /// // │ ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ memory.len() ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘
120 | /// // └→ prefix() └→ memory
121 | /// let memory = my_alloc.alloc(layout)?;
122 | ///
123 | /// assert_eq!(memory.len(), 112);
124 | /// unsafe {
125 | /// assert_eq!(
126 | /// Alloc::prefix(memory.as_non_null_ptr(), layout).cast().as_ptr(),
127 | /// memory.as_mut_ptr().sub(16)
128 | /// );
129 | /// assert_eq!(Alloc::suffix(memory.as_non_null_ptr(), layout), NonNull::dangling());
130 | /// }
131 | /// # Ok::<(), core::alloc::AllocErr>(())
132 | /// ```
133 | ///
134 | /// This results in only `4` bytes unused memory.
135 | ///
136 | /// If `Prefix` is a zero-sized type, this results in a waste of memory:
137 | ///
138 | /// ```
139 | /// # #![feature(allocator_api, slice_ptr_get, slice_ptr_len)]
140 | /// # use alloc_compose::{Affix, Chunk};
141 | /// # use std::alloc::{Layout, System, AllocRef};
142 | /// # use core::ptr::NonNull;
143 | /// # type Suffix = [u64; 2];
144 | /// type Alloc = Affix, (), Suffix>;
145 | /// # let layout = Layout::from_size_align(28, 8).unwrap();
146 | ///
147 | /// let mut my_alloc = Alloc::default();
148 | ///
149 | /// // 0 28 32 48 64 128
150 | /// // ╞════ requested memory ═════╡ ╞═══ Suffix ════╡ │ │
151 | /// // ┢┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳╈┳┳┳╈┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳╅┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┼┬┬╌╌╌╌┬┬┤
152 | /// // ┡┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻╇┻┻┻╇┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┹┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴╌╌╌╌┴┴┘
153 | /// // ├┄┄┄┄┄┄ layout.size() ┄┄┄┄┄┄┘ │
154 | /// // ├┄┄┄┄┄┄┄┄ memory.len() ┄┄┄┄┄┄┄┄┄┤
155 | /// // └→ memory └→ suffix()
156 | /// let memory = my_alloc.alloc(layout)?;
157 | ///
158 | /// assert_eq!(memory.len(), 32);
159 | /// unsafe {
160 | /// assert_eq!(Alloc::prefix(memory.as_non_null_ptr(), layout), NonNull::dangling());
161 | /// assert_eq!(
162 | /// Alloc::suffix(memory.as_non_null_ptr(), layout).cast().as_ptr(),
163 | /// memory.as_mut_ptr().add(32)
164 | /// );
165 | /// }
166 | /// # Ok::<(), core::alloc::AllocErr>(())
167 | /// ```
168 | ///
169 | /// This results in 80 bytes unused memory. As can be seen, if possible a prefix should be
170 | /// preferred to the suffix.
171 | ///
172 | /// If both, `Prefix` and `Suffix` are ZSTs, this behaves like the parent allocator:
173 | ///
174 | /// ```
175 | /// # #![feature(allocator_api, slice_ptr_get, slice_ptr_len)]
176 | /// # use alloc_compose::{Affix, Chunk};
177 | /// # use std::alloc::{Layout, System, AllocRef};
178 | /// # use core::ptr::NonNull;
179 | /// # type Suffix = [u64; 2];
180 | /// type Alloc = Affix, (), ()>;
181 | /// # let layout = Layout::from_size_align(28, 8).unwrap();
182 | ///
183 | /// let mut my_alloc = Alloc::default();
184 | ///
185 | /// // 0 28 32 48 64 128
186 | /// // ╞════ requested memory ═════╡ │ │ │ │
187 | /// // ┢┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳╈┳┳┳╈┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳╈┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳╈┳┳╍╍╍╍┳┳┪
188 | /// // ┡┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻╇┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻╍╍╍╍┻┻┩
189 | /// // ├┄┄┄┄┄┄ layout.size() ┄┄┄┄┄┄┘ │
190 | /// // ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ memory.len() ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘
191 | /// // └→ memory
192 | /// let memory = my_alloc.alloc(layout)?;
193 | ///
194 | /// assert_eq!(memory.len(), 128);
195 | /// unsafe {
196 | /// assert_eq!(Alloc::prefix(memory.as_non_null_ptr(), layout), NonNull::dangling());
197 | /// assert_eq!(Alloc::suffix(memory.as_non_null_ptr(), layout), NonNull::dangling());
198 | /// }
199 | /// # Ok::<(), core::alloc::AllocErr>(())
200 | /// ```
201 | pub struct Affix {
202 | /// The parent allocator to be used as backend
203 | pub parent: Alloc,
204 | _prefix: PhantomData,
205 | _suffix: PhantomData,
206 | }
207 |
208 | impl fmt::Debug for Affix {
209 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210 | f.debug_struct("Affix")
211 | .field("parent", &self.parent)
212 | .finish()
213 | }
214 | }
215 |
216 | impl Default for Affix {
217 | fn default() -> Self {
218 | Self::new(Alloc::default())
219 | }
220 | }
221 |
222 | impl Clone for Affix {
223 | fn clone(&self) -> Self {
224 | Self::new(self.parent.clone())
225 | }
226 | }
227 |
228 | impl Copy for Affix {}
229 |
230 | impl PartialEq for Affix {
231 | fn eq(&self, other: &Self) -> bool {
232 | self.parent.eq(&other.parent)
233 | }
234 | }
235 |
236 | impl Eq for Affix {}
237 |
238 | unsafe impl Send for Affix {}
239 | unsafe impl Sync for Affix {}
240 | impl Unpin for Affix {}
241 |
242 | impl Affix {
243 | pub const fn new(parent: Alloc) -> Self {
244 | Self {
245 | parent,
246 | _prefix: PhantomData,
247 | _suffix: PhantomData,
248 | }
249 | }
250 |
251 | fn allocation_layout(layout: Layout) -> Option<(Layout, usize, usize)> {
252 | let (layout, prefix_offset) = Layout::new::().extend(layout).ok()?;
253 | let (layout, suffix_offset) = layout.extend(Layout::new::()).ok()?;
254 | Some((layout, prefix_offset, suffix_offset))
255 | }
256 |
257 | /// Returns a pointer to the prefix.
258 | ///
259 | /// # Safety
260 | ///
261 | /// * `ptr` must denote a block of memory *[currently allocated]* via this allocator, and
262 | /// * `layout` must *[fit]* that block of memory.
263 | ///
264 | /// [currently allocated]: https://doc.rust-lang.org/nightly/core/alloc/trait.AllocRef.html#currently-allocated-memory
265 | /// [fit]: https://doc.rust-lang.org/nightly/core/alloc/trait.AllocRef.html#memory-fitting
266 | pub unsafe fn prefix(ptr: NonNull, layout: Layout) -> NonNull {
267 | if mem::size_of::() == 0 {
268 | NonNull::dangling()
269 | } else {
270 | let (_, prefix, _) = Self::allocation_layout(layout).unwrap();
271 | NonNull::new_unchecked(ptr.as_ptr().sub(prefix)).cast()
272 | }
273 | }
274 |
275 | /// Returns a pointer to the suffix.
276 | ///
277 | /// # Safety
278 | ///
279 | /// * `ptr` must denote a block of memory *[currently allocated]* via this allocator, and
280 | /// * `layout` must *[fit]* that block of memory.
281 | ///
282 | /// [currently allocated]: https://doc.rust-lang.org/nightly/core/alloc/trait.AllocRef.html#currently-allocated-memory
283 | /// [fit]: https://doc.rust-lang.org/nightly/core/alloc/trait.AllocRef.html#memory-fitting
284 | pub unsafe fn suffix(ptr: NonNull, layout: Layout) -> NonNull {
285 | if mem::size_of::() == 0 {
286 | NonNull::dangling()
287 | } else {
288 | let (_, prefix, suffix) = Self::allocation_layout(layout).unwrap();
289 | NonNull::new_unchecked(ptr.as_ptr().add(suffix - prefix)).cast()
290 | }
291 | }
292 |
293 | fn create_ptr(ptr: NonNull<[u8]>, offset_prefix: usize, offset_suffix: usize) -> NonNull<[u8]> {
294 | let len = if mem::size_of::() == 0 {
295 | ptr.len() - offset_prefix
296 | } else {
297 | offset_suffix - offset_prefix
298 | };
299 | let ptr = unsafe { NonNull::new_unchecked(ptr.as_mut_ptr().add(offset_prefix)) };
300 |
301 | NonNull::slice_from_raw_parts(ptr, len)
302 | }
303 |
304 | #[inline]
305 | fn alloc_impl(
306 | layout: Layout,
307 | alloc: impl FnOnce(Layout) -> Result, AllocErr>,
308 | ) -> Result, AllocErr> {
309 | let (layout, offset_prefix, offset_suffix) =
310 | Self::allocation_layout(layout).ok_or(AllocErr)?;
311 |
312 | Ok(Self::create_ptr(
313 | alloc(layout)?,
314 | offset_prefix,
315 | offset_suffix,
316 | ))
317 | }
318 |
319 | #[inline]
320 | unsafe fn grow_impl(
321 | old_ptr: NonNull,
322 | old_layout: Layout,
323 | new_size: usize,
324 | init: AllocInit,
325 | grow: impl FnOnce(NonNull, Layout, usize) -> Result, AllocErr>,
326 | ) -> Result, AllocErr> {
327 | let (old_alloc_layout, old_offset_prefix, old_offset_suffix) =
328 | Self::allocation_layout(old_layout).ok_or(AllocErr)?;
329 | let old_base_ptr = NonNull::new_unchecked(old_ptr.as_ptr().sub(old_offset_prefix));
330 |
331 | let suffix = Self::suffix(old_ptr, old_layout)
332 | .cast::>()
333 | .as_ptr()
334 | .read();
335 |
336 | let new_layout =
337 | Layout::from_size_align(new_size, old_layout.align()).map_err(|_| AllocErr)?;
338 | let (new_alloc_layout, new_offset_prefix, new_offset_suffix) =
339 | Self::allocation_layout(new_layout).ok_or(AllocErr)?;
340 |
341 | let new_base_ptr = grow(old_base_ptr, old_alloc_layout, new_alloc_layout.size())?;
342 |
343 | if init == AllocInit::Zeroed {
344 | ptr::write_bytes(
345 | new_base_ptr
346 | .as_non_null_ptr()
347 | .as_ptr()
348 | .add(old_offset_suffix),
349 | 0,
350 | mem::size_of::(),
351 | );
352 | }
353 |
354 | let new_ptr = Self::create_ptr(new_base_ptr, new_offset_prefix, new_offset_suffix);
355 |
356 | Self::suffix(new_ptr.as_non_null_ptr(), new_layout)
357 | .cast::>()
358 | .as_ptr()
359 | .write(suffix);
360 |
361 | Ok(new_ptr)
362 | }
363 |
364 | #[inline]
365 | unsafe fn shrink_impl(
366 | old_ptr: NonNull,
367 | old_layout: Layout,
368 | new_size: usize,
369 | shrink: impl FnOnce(NonNull, Layout, usize) -> Result, AllocErr>,
370 | ) -> Result, AllocErr> {
371 | let (old_alloc_layout, old_offset_prefix, _) =
372 | Self::allocation_layout(old_layout).ok_or(AllocErr)?;
373 | let old_base_ptr = NonNull::new_unchecked(old_ptr.as_ptr().sub(old_offset_prefix));
374 |
375 | let suffix = Self::suffix(old_ptr, old_layout)
376 | .cast::>()
377 | .as_ptr()
378 | .read();
379 |
380 | let new_layout =
381 | Layout::from_size_align(new_size, old_layout.align()).map_err(|_| AllocErr)?;
382 | let (new_alloc_layout, new_offset_prefix, new_offset_suffix) =
383 | Self::allocation_layout(new_layout).ok_or(AllocErr)?;
384 |
385 | let new_base_ptr = shrink(old_base_ptr, old_alloc_layout, new_alloc_layout.size())?;
386 |
387 | let new_ptr = Self::create_ptr(new_base_ptr, new_offset_prefix, new_offset_suffix);
388 |
389 | Self::suffix(new_ptr.as_non_null_ptr(), new_layout)
390 | .cast::>()
391 | .as_ptr()
392 | .write(suffix);
393 |
394 | Ok(new_ptr)
395 | }
396 | }
397 |
398 | unsafe impl AllocRef for Affix
399 | where
400 | Alloc: AllocRef,
401 | {
402 | impl_alloc_ref!(parent);
403 |
404 | unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) {
405 | let (layout, prefix_offset, _) = Self::allocation_layout(layout).unwrap();
406 | let base_ptr = ptr.as_ptr().sub(prefix_offset);
407 | self.parent
408 | .dealloc(NonNull::new_unchecked(base_ptr), layout)
409 | }
410 | }
411 |
412 | unsafe impl AllocAll for Affix
413 | where
414 | Alloc: AllocAll,
415 | {
416 | impl_alloc_all!(parent);
417 | }
418 |
419 | unsafe impl ReallocInPlace for Affix
420 | where
421 | Alloc: ReallocInPlace,
422 | {
423 | impl_realloc_in_place!(parent);
424 | }
425 |
426 | #[cfg(test)]
427 | mod tests {
428 | #![allow(clippy::wildcard_imports)]
429 | use super::*;
430 | use crate::helper::tracker;
431 | use core::fmt;
432 | use std::alloc::System;
433 |
434 | #[allow(clippy::too_many_lines)]
435 | fn test_alloc(
436 | prefix: Prefix,
437 | layout: Layout,
438 | suffix: Suffix,
439 | offset_prefix: usize,
440 | offset_suffix: usize,
441 | ) where
442 | Prefix: fmt::Debug + Copy + PartialEq,
443 | Suffix: fmt::Debug + Copy + PartialEq,
444 | {
445 | unsafe {
446 | let mut alloc = tracker(Affix::<_, Prefix, Suffix>::new(tracker(System)));
447 | let memory = alloc
448 | .alloc_zeroed(layout)
449 | .unwrap_or_else(|_| panic!("Could not allocate {} bytes", layout.size()));
450 |
451 | if mem::size_of::() == 0 {
452 | assert_eq!(
453 | Affix::::prefix(memory.as_non_null_ptr(), layout),
454 | NonNull::dangling()
455 | );
456 | } else {
457 | assert_eq!(
458 | Affix::::prefix(memory.as_non_null_ptr(), layout)
459 | .cast()
460 | .as_ptr(),
461 | memory.as_mut_ptr().sub(offset_prefix)
462 | );
463 | }
464 | if mem::size_of::() == 0 {
465 | assert_eq!(
466 | Affix::::suffix(memory.as_non_null_ptr(), layout),
467 | NonNull::dangling()
468 | );
469 | } else {
470 | assert_eq!(
471 | Affix::::suffix(memory.as_non_null_ptr(), layout)
472 | .cast()
473 | .as_ptr(),
474 | memory.as_mut_ptr().add(offset_suffix)
475 | );
476 | }
477 |
478 | Affix::::prefix(memory.as_non_null_ptr(), layout)
479 | .as_ptr()
480 | .write(prefix);
481 | Affix::::suffix(memory.as_non_null_ptr(), layout)
482 | .as_ptr()
483 | .write(suffix);
484 |
485 | assert_eq!(
486 | Affix::::prefix(memory.as_non_null_ptr(), layout).as_ref(),
487 | &prefix
488 | );
489 | assert_eq!(
490 | Affix::::suffix(memory.as_non_null_ptr(), layout).as_ref(),
491 | &suffix
492 | );
493 |
494 | let old_size = memory.len();
495 | let memory = alloc
496 | .grow_zeroed(memory.as_non_null_ptr(), layout, memory.len() * 2)
497 | .expect("Could not grow allocation");
498 | let layout =
499 | Layout::from_size_align(memory.len(), layout.align()).expect("Invalid layout");
500 |
501 | for i in old_size..memory.len() {
502 | assert_eq!(*memory.get_unchecked_mut(i).as_ref(), 0);
503 | }
504 |
505 | assert_eq!(
506 | Affix::::prefix(memory.as_non_null_ptr(), layout).as_ref(),
507 | &prefix
508 | );
509 | assert_eq!(
510 | Affix::::suffix(memory.as_non_null_ptr(), layout).as_ref(),
511 | &suffix
512 | );
513 |
514 | let memory = alloc
515 | .shrink(memory.as_non_null_ptr(), layout, layout.size())
516 | .expect("Could not shrink allocation");
517 | let layout =
518 | Layout::from_size_align(memory.len(), layout.align()).expect("Invalid layout");
519 |
520 | assert_eq!(
521 | Affix::::prefix(memory.as_non_null_ptr(), layout).as_ref(),
522 | &prefix
523 | );
524 | assert_eq!(
525 | Affix::::suffix(memory.as_non_null_ptr(), layout).as_ref(),
526 | &suffix
527 | );
528 |
529 | alloc.dealloc(memory.as_non_null_ptr(), layout);
530 | }
531 | }
532 |
533 | #[test]
534 | fn test_alloc_u16_u32_u16() {
535 | test_alloc::(0xDEDE, Layout::new::(), 0xEFEF, 4, 4)
536 | }
537 |
538 | #[test]
539 | fn test_alloc_zst_u32_zst() {
540 | test_alloc::<(), ()>((), Layout::new::(), (), 0, 0)
541 | }
542 |
543 | #[test]
544 | fn test_alloc_zst_u32_u16() {
545 | test_alloc::<(), u16>((), Layout::new::(), 0xEFEF, 0, 4)
546 | }
547 |
548 | #[test]
549 | fn test_alloc_u16_u64_zst() {
550 | test_alloc::(0xDEDE, Layout::new::(), (), 4, 0)
551 | }
552 |
553 | #[repr(align(1024))]
554 | #[derive(Debug, Copy, Clone, PartialEq)]
555 | struct AlignTo1024 {
556 | a: u16,
557 | }
558 |
559 | #[repr(align(64))]
560 | #[derive(Debug, Copy, Clone, PartialEq)]
561 | struct AlignTo64;
562 |
563 | #[test]
564 | fn test_alloc_a1024_u32_zst() {
565 | test_alloc::(AlignTo1024 { a: 0xDEDE }, Layout::new::(), (), 1024, 0)
566 | }
567 |
568 | #[test]
569 | fn test_alloc_u16_u32_a1024() {
570 | test_alloc::(
571 | 0xDEDE,
572 | Layout::new::(),
573 | AlignTo1024 { a: 0xEFEF },
574 | 4,
575 | 1020,
576 | )
577 | }
578 |
579 | #[test]
580 | fn test_alloc_a64_u32_zst() {
581 | test_alloc::(AlignTo64, Layout::new::(), (), 0, 0)
582 | }
583 |
584 | #[test]
585 | fn test_alloc_u16_u32_a64() {
586 | test_alloc::(0xDEDE, Layout::new::(), AlignTo64, 4, 0)
587 | }
588 | }
589 |
--------------------------------------------------------------------------------
/src/callback_ref.rs:
--------------------------------------------------------------------------------
1 | use core::{
2 | alloc::{AllocError, Layout},
3 | ptr::NonNull,
4 | };
5 |
6 | /// Backend for the [`Proxy`] allocator.
7 | ///
8 | /// As `Callback` is used in `Proxy` and `AllocRef` requires, that a cloned allocator must
9 | /// behave like the same allocator, `Clone` must not be implemented on types, which don't
10 | /// have a shared state. It's possible to use a reference by calling [`by_ref`] or to
11 | /// wrapping them into `Rc` or `Arc` in order to make them cloneable instead. Note, that
12 | /// `Box`, `Rc`, and `Arc` requires the `"alloc"`-feature to be enabled.
13 | ///
14 | /// [`by_ref`]: CallbackRef::by_ref
15 | /// [`Proxy`]: crate::Proxy
16 | ///
17 | /// # Safety
18 | /// * `Clone` must not be implemented on types, which don't have a shared state.
19 | #[allow(unused_variables)]
20 | pub unsafe trait CallbackRef {
21 | /// Called before [`alloc`] was invoked.
22 | ///
23 | /// [`alloc`]: core::alloc::AllocRef::alloc
24 | #[inline]
25 | fn before_allocate(&self, layout: Layout) {}
26 |
27 | /// Called after [`alloc`] was invoked.
28 | ///
29 | /// [`alloc`]: core::alloc::AllocRef::alloc
30 | #[inline]
31 | fn after_allocate(&self, layout: Layout, result: Result, AllocError>) {}
32 |
33 | /// Called before [`alloc_zeroed`] was invoked.
34 | ///
35 | /// [`alloc_zeroed`]: core::alloc::AllocRef::alloc_zeroed
36 | #[inline]
37 | fn before_allocate_zeroed(&self, layout: Layout) {}
38 |
39 | /// Called after [`alloc_zeroed`] was invoked.
40 | ///
41 | /// [`alloc_zeroed`]: core::alloc::AllocRef::alloc_zeroed
42 | #[inline]
43 | fn after_allocate_zeroed(&self, layout: Layout, result: Result, AllocError>) {}
44 |
45 | /// Called before [`allocate_all`] was invoked.
46 | ///
47 | /// [`allocate_all`]: crate::AllocateAll::allocate_all
48 | #[inline]
49 | fn before_allocate_all(&self) {}
50 |
51 | /// Called after [`allocate_all`] was invoked.
52 | ///
53 | /// [`allocate_all`]: crate::AllocateAll::allocate_all
54 | #[inline]
55 | fn after_allocate_all(&self, result: Result, AllocError>) {}
56 |
57 | /// Called before [`allocate_all_zeroed`] was invoked.
58 | ///
59 | /// [`allocate_all_zeroed`]: crate::AllocateAll::allocate_all_zeroed
60 | #[inline]
61 | fn before_allocate_all_zeroed(&self) {}
62 |
63 | /// Called after [`allocate_all_zeroed`] was invoked.
64 | ///
65 | /// [`allocate_all_zeroed`]: crate::AllocateAll::allocate_all_zeroed
66 | #[inline]
67 | fn after_allocate_all_zeroed(&self, result: Result, AllocError>) {}
68 |
69 | /// Called before [`dealloc`] was invoked.
70 | ///
71 | /// [`dealloc`]: core::alloc::AllocRef::dealloc
72 | #[inline]
73 | fn before_deallocate(&self, ptr: NonNull, layout: Layout) {}
74 |
75 | /// Called after [`dealloc`] was invoked.
76 | ///
77 | /// [`dealloc`]: core::alloc::AllocRef::dealloc
78 | #[inline]
79 | fn after_deallocate(&self, ptr: NonNull, layout: Layout) {}
80 |
81 | /// Called before [`deallocate_all`] was invoked.
82 | ///
83 | /// [`deallocate_all`]: crate::AllocateAll::deallocate_all
84 | #[inline]
85 | fn before_deallocate_all(&self) {}
86 |
87 | /// Called after [`deallocate_all`] was invoked.
88 | ///
89 | /// [`deallocate_all`]: crate::AllocateAll::deallocate_all
90 | #[inline]
91 | fn after_deallocate_all(&self) {}
92 |
93 | /// Called before [`grow`] was invoked.
94 | ///
95 | /// [`grow`]: core::alloc::AllocRef::grow
96 | #[inline]
97 | fn before_grow(&self, ptr: NonNull, old_layout: Layout, new_layout: Layout) {}
98 |
99 | /// Called after [`grow`] was invoked.
100 | ///
101 | /// [`grow`]: core::alloc::AllocRef::grow
102 | #[inline]
103 | fn after_grow(
104 | &self,
105 | ptr: NonNull,
106 | old_layout: Layout,
107 | new_layout: Layout,
108 | result: Result, AllocError>,
109 | ) {
110 | }
111 |
112 | /// Called before [`grow_zeroed`] was invoked.
113 | ///
114 | /// [`grow_zeroed`]: core::alloc::AllocRef::grow_zeroed
115 | #[inline]
116 | fn before_grow_zeroed(&self, ptr: NonNull, old_layout: Layout, new_layout: Layout) {}
117 |
118 | /// Called after [`grow_zeroed`] was invoked.
119 | ///
120 | /// [`grow_zeroed`]: core::alloc::AllocRef::grow_zeroed
121 | #[inline]
122 | fn after_grow_zeroed(
123 | &self,
124 | ptr: NonNull,
125 | old_layout: Layout,
126 | new_layout: Layout,
127 | result: Result, AllocError>,
128 | ) {
129 | }
130 |
131 | /// Called before [`grow_in_place`] was invoked.
132 | ///
133 | /// [`grow_in_place`]: crate::ReallocateInPlace::grow_in_place
134 | #[inline]
135 | fn before_grow_in_place(&self, ptr: NonNull, old_layout: Layout, new_layout: Layout) {}
136 |
137 | /// Called after [`grow_in_place`] was invoked.
138 | ///
139 | /// [`grow_in_place`]: crate::ReallocateInPlace::grow_in_place
140 | #[inline]
141 | fn after_grow_in_place(
142 | &self,
143 | ptr: NonNull,
144 | old_layout: Layout,
145 | new_layout: Layout,
146 | result: Result,
147 | ) {
148 | }
149 |
150 | /// Called before [`grow_in_place_zeroed`] was invoked.
151 | ///
152 | /// [`grow_in_place_zeroed`]: crate::ReallocateInPlace::grow_in_place_zeroed
153 | #[inline]
154 | fn before_grow_in_place_zeroed(
155 | &self,
156 | ptr: NonNull,
157 | old_layout: Layout,
158 | new_layout: Layout,
159 | ) {
160 | }
161 |
162 | /// Called after [`grow_in_place_zeroed`] was invoked.
163 | ///
164 | /// [`grow_in_place_zeroed`]: crate::ReallocateInPlace::grow_in_place_zeroed
165 | #[inline]
166 | fn after_grow_in_place_zeroed(
167 | &self,
168 | ptr: NonNull,
169 | old_layout: Layout,
170 | new_layout: Layout,
171 | result: Result,
172 | ) {
173 | }
174 |
175 | /// Called before [`shrink`] was invoked.
176 | ///
177 | /// [`shrink`]: core::alloc::AllocRef::shrink
178 | #[inline]
179 | fn before_shrink(&self, ptr: NonNull, old_layout: Layout, new_layout: Layout) {}
180 |
181 | /// Called after [`shrink`] was invoked.
182 | ///
183 | /// [`shrink`]: core::alloc::AllocRef::shrink
184 | #[inline]
185 | fn after_shrink(
186 | &self,
187 | ptr: NonNull,
188 | old_layout: Layout,
189 | new_layout: Layout,
190 | result: Result, AllocError>,
191 | ) {
192 | }
193 |
194 | /// Called before [`shrink_in_place`] was invoked.
195 | ///
196 | /// [`shrink_in_place`]: crate::ReallocateInPlace::shrink_in_place
197 | #[inline]
198 | fn before_shrink_in_place(&self, ptr: NonNull, old_layout: Layout, new_layout: Layout) {}
199 |
200 | /// Called after [`shrink_in_place`] was invoked.
201 | ///
202 | /// [`shrink_in_place`]: crate::ReallocateInPlace::shrink_in_place
203 | #[inline]
204 | fn after_shrink_in_place(
205 | &self,
206 | ptr: NonNull,
207 | old_layout: Layout,
208 | new_layout: Layout,
209 | result: Result,
210 | ) {
211 | }
212 |
213 | /// Called before [`owns`] was invoked.
214 | ///
215 | /// [`owns`]: crate::Owns::owns
216 | #[inline]
217 | fn before_owns(&self) {}
218 |
219 | /// Called after [`owns`] was invoked.
220 | ///
221 | /// [`owns`]: crate::Owns::owns
222 | #[inline]
223 | fn after_owns(&self, success: bool) {}
224 |
225 | /// Creates a "by reference" adaptor for this instance of `CallbackRef`.
226 | ///
227 | /// The returned adaptor also implements `CallbackRef` and will simply borrow this.
228 | #[inline]
229 | fn by_ref(&self) -> &Self {
230 | self
231 | }
232 | }
233 |
234 | macro_rules! impl_alloc_stats {
235 | ($(#[$meta:meta])* $ty:ty) => {
236 | $(#[$meta])*
237 | unsafe impl CallbackRef for $ty where C: CallbackRef + ?Sized {
238 | #[inline]
239 | fn before_allocate(&self, layout: Layout) {
240 | (**self).before_allocate(layout)
241 | }
242 |
243 | #[inline]
244 | fn after_allocate(&self, layout: Layout, result: Result, AllocError>) {
245 | (**self).after_allocate(layout, result)
246 | }
247 |
248 | #[inline]
249 | fn before_allocate_zeroed(&self, layout: Layout) {
250 | (**self).before_allocate_zeroed(layout)
251 | }
252 |
253 | #[inline]
254 | fn after_allocate_zeroed(&self, layout: Layout, result: Result, AllocError>) {
255 | (**self).after_allocate_zeroed(layout, result)
256 | }
257 |
258 | #[inline]
259 | fn before_allocate_all(&self) {
260 | (**self).before_allocate_all()
261 | }
262 |
263 | #[inline]
264 | fn after_allocate_all(&self, result: Result, AllocError>) {
265 | (**self).after_allocate_all(result)
266 | }
267 |
268 | #[inline]
269 | fn before_allocate_all_zeroed(&self) {
270 | (**self).before_allocate_all_zeroed()
271 | }
272 |
273 | #[inline]
274 | fn after_allocate_all_zeroed(
275 | &self,
276 | result: Result, AllocError>,
277 | ) {
278 | (**self).after_allocate_all_zeroed(result)
279 | }
280 |
281 | #[inline]
282 | fn before_deallocate(&self, ptr: NonNull, layout: Layout) {
283 | (**self).before_deallocate(ptr, layout)
284 | }
285 |
286 | #[inline]
287 | fn after_deallocate(&self, ptr: NonNull, layout: Layout) {
288 | (**self).after_deallocate(ptr, layout)
289 | }
290 |
291 | #[inline]
292 | fn before_deallocate_all(&self) {
293 | (**self).before_deallocate_all()
294 | }
295 |
296 | #[inline]
297 | fn after_deallocate_all(&self) {
298 | (**self).after_deallocate_all()
299 | }
300 |
301 | #[inline]
302 | fn before_grow(&self, ptr: NonNull, old_layout: Layout, new_layout: Layout) {
303 | (**self).before_grow(ptr, old_layout, new_layout)
304 | }
305 |
306 | #[inline]
307 | fn after_grow(
308 | &self,
309 | ptr: NonNull,
310 | old_layout: Layout,
311 | new_layout: Layout,
312 | result: Result, AllocError>,
313 | ) {
314 | (**self).after_grow(ptr, old_layout, new_layout, result)
315 | }
316 |
317 | #[inline]
318 | fn before_grow_zeroed(&self, ptr: NonNull,
319 | old_layout: Layout,
320 | new_layout: Layout,) {
321 | (**self).before_grow_zeroed(ptr, old_layout, new_layout)
322 | }
323 |
324 | #[inline]
325 | fn after_grow_zeroed(
326 | &self,
327 | ptr: NonNull,
328 | old_layout: Layout,
329 | new_layout: Layout,
330 | result: Result, AllocError>,
331 | ) {
332 | (**self).after_grow_zeroed(ptr, old_layout, new_layout, result)
333 | }
334 |
335 | #[inline]
336 | fn before_grow_in_place(&self, ptr: NonNull,
337 | old_layout: Layout,
338 | new_layout: Layout,) {
339 | (**self).before_grow_in_place(ptr, old_layout, new_layout)
340 | }
341 |
342 | #[inline]
343 | fn after_grow_in_place(
344 | &self,
345 | ptr: NonNull,
346 | old_layout: Layout,
347 | new_layout: Layout,
348 | result: Result,
349 | ) {
350 | (**self).after_grow_in_place(ptr, old_layout, new_layout, result)
351 | }
352 |
353 | #[inline]
354 | fn before_grow_in_place_zeroed(
355 | &self,
356 | ptr: NonNull,
357 | old_layout: Layout,
358 | new_layout: Layout,
359 | ) {
360 | (**self).before_grow_in_place_zeroed(ptr, old_layout, new_layout)
361 | }
362 |
363 | #[inline]
364 | fn after_grow_in_place_zeroed(
365 | &self,
366 | ptr: NonNull,
367 | old_layout: Layout,
368 | new_layout: Layout,
369 | result: Result,
370 | ) {
371 | (**self).after_grow_in_place_zeroed(ptr, old_layout, new_layout, result)
372 | }
373 |
374 | #[inline]
375 | fn before_shrink(&self, ptr: NonNull,
376 | old_layout: Layout,
377 | new_layout: Layout,) {
378 | (**self).before_shrink(ptr, old_layout, new_layout)
379 | }
380 |
381 | #[inline]
382 | fn after_shrink(
383 | &self,
384 | ptr: NonNull,
385 | old_layout: Layout,
386 | new_layout: Layout,
387 | result: Result, AllocError>,
388 | ) {
389 | (**self).after_shrink(ptr, old_layout, new_layout, result)
390 | }
391 |
392 | #[inline]
393 | fn before_shrink_in_place(&self, ptr: NonNull,
394 | old_layout: Layout,
395 | new_layout: Layout,) {
396 | (**self).before_shrink_in_place(ptr, old_layout, new_layout)
397 | }
398 |
399 | #[inline]
400 | fn after_shrink_in_place(
401 | &self,
402 | ptr: NonNull,
403 | old_layout: Layout,
404 | new_layout: Layout,
405 | result: Result,
406 | ) {
407 | (**self).after_shrink_in_place(ptr, old_layout, new_layout, result)
408 | }
409 |
410 | #[inline]
411 | fn before_owns(&self) {
412 | (**self).before_owns()
413 | }
414 |
415 | #[inline]
416 | fn after_owns(&self, success: bool) {
417 | (**self).after_owns(success)
418 | }
419 | }
420 | };
421 | }
422 |
423 | impl_alloc_stats!(&C);
424 | #[cfg(any(doc, feature = "alloc"))]
425 | impl_alloc_stats!(#[cfg_attr(doc, doc(cfg(feature = "alloc")))] alloc::boxed::Box);
426 | #[cfg(any(doc, feature = "alloc"))]
427 | impl_alloc_stats!(#[cfg_attr(doc, doc(cfg(feature = "alloc")))] alloc::rc::Rc);
428 | #[cfg(any(doc, feature = "alloc"))]
429 | impl_alloc_stats!(#[cfg_attr(doc, doc(cfg(feature = "alloc")))] alloc::sync::Arc);
430 |
431 | #[cfg(test)]
432 | mod tests {
433 | use crate::CallbackRef;
434 | use alloc::{boxed::Box, rc::Rc, sync::Arc};
435 | use core::{
436 | alloc::{AllocError, Layout},
437 | cell::Cell,
438 | ptr::NonNull,
439 | };
440 |
441 | #[derive(Default)]
442 | struct Callback {
443 | before_allocate: Cell,
444 | after_allocate: Cell,
445 | before_allocate_zeroed: Cell,
446 | after_allocate_zeroed: Cell,
447 | before_allocate_all: Cell,
448 | after_allocate_all: Cell,
449 | before_allocate_all_zeroed: Cell,
450 | after_allocate_all_zeroed: Cell,
451 | before_deallocate: Cell,
452 | after_deallocate: Cell,
453 | before_deallocate_all: Cell,
454 | after_deallocate_all: Cell,
455 | before_grow: Cell,
456 | after_grow: Cell,
457 | before_grow_zeroed: Cell,
458 | after_grow_zeroed: Cell,
459 | before_grow_in_place: Cell,
460 | after_grow_in_place: Cell,
461 | before_grow_in_place_zeroed: Cell,
462 | after_grow_in_place_zeroed: Cell,
463 | before_shrink: Cell,
464 | after_shrink: Cell,
465 | before_shrink_in_place: Cell,
466 | after_shrink_in_place: Cell,
467 | before_owns: Cell,
468 | after_owns: Cell,
469 | }
470 |
471 | unsafe impl CallbackRef for Callback {
472 | fn before_allocate(&self, _layout: Layout) {
473 | self.before_allocate.set(self.before_allocate.get() + 1)
474 | }
475 | fn after_allocate(&self, _layout: Layout, _result: Result, AllocError>) {
476 | self.after_allocate.set(self.after_allocate.get() + 1)
477 | }
478 | fn before_allocate_zeroed(&self, _layout: Layout) {
479 | self.before_allocate_zeroed
480 | .set(self.before_allocate_zeroed.get() + 1)
481 | }
482 | fn after_allocate_zeroed(
483 | &self,
484 | _layout: Layout,
485 | _result: Result, AllocError>,
486 | ) {
487 | self.after_allocate_zeroed
488 | .set(self.after_allocate_zeroed.get() + 1)
489 | }
490 | fn before_allocate_all(&self) {
491 | self.before_allocate_all
492 | .set(self.before_allocate_all.get() + 1)
493 | }
494 | fn after_allocate_all(&self, _result: Result, AllocError>) {
495 | self.after_allocate_all
496 | .set(self.after_allocate_all.get() + 1)
497 | }
498 | fn before_allocate_all_zeroed(&self) {
499 | self.before_allocate_all_zeroed
500 | .set(self.before_allocate_all_zeroed.get() + 1)
501 | }
502 | fn after_allocate_all_zeroed(&self, _result: Result, AllocError>) {
503 | self.after_allocate_all_zeroed
504 | .set(self.after_allocate_all_zeroed.get() + 1)
505 | }
506 | fn before_deallocate(&self, _ptr: NonNull, _layout: Layout) {
507 | self.before_deallocate.set(self.before_deallocate.get() + 1)
508 | }
509 | fn after_deallocate(&self, _ptr: NonNull, _layout: Layout) {
510 | self.after_deallocate.set(self.after_deallocate.get() + 1)
511 | }
512 | fn before_deallocate_all(&self) {
513 | self.before_deallocate_all
514 | .set(self.before_deallocate_all.get() + 1)
515 | }
516 | fn after_deallocate_all(&self) {
517 | self.after_deallocate_all
518 | .set(self.after_deallocate_all.get() + 1)
519 | }
520 | fn before_grow(&self, _ptr: NonNull, _old_layout: Layout, _new_layout: Layout) {
521 | self.before_grow.set(self.before_grow.get() + 1)
522 | }
523 | fn after_grow(
524 | &self,
525 | _ptr: NonNull,
526 | _old_layout: Layout,
527 | _new_layout: Layout,
528 | _result: Result, AllocError>,
529 | ) {
530 | self.after_grow.set(self.after_grow.get() + 1)
531 | }
532 | fn before_grow_zeroed(&self, _ptr: NonNull, _old_layout: Layout, _new_layout: Layout) {
533 | self.before_grow_zeroed
534 | .set(self.before_grow_zeroed.get() + 1)
535 | }
536 | fn after_grow_zeroed(
537 | &self,
538 | _ptr: NonNull,
539 | _old_layout: Layout,
540 | _new_layout: Layout,
541 | _result: Result, AllocError>,
542 | ) {
543 | self.after_grow_zeroed.set(self.after_grow_zeroed.get() + 1)
544 | }
545 | fn before_grow_in_place(
546 | &self,
547 | _ptr: NonNull,
548 | _old_layout: Layout,
549 | _new_layout: Layout,
550 | ) {
551 | self.before_grow_in_place
552 | .set(self.before_grow_in_place.get() + 1)
553 | }
554 | fn after_grow_in_place(
555 | &self,
556 | _ptr: NonNull,
557 | _old_layout: Layout,
558 | _new_layout: Layout,
559 | _result: Result,
560 | ) {
561 | self.after_grow_in_place
562 | .set(self.after_grow_in_place.get() + 1)
563 | }
564 | fn before_grow_in_place_zeroed(
565 | &self,
566 | _ptr: NonNull,
567 | _old_layout: Layout,
568 | _new_layout: Layout,
569 | ) {
570 | self.before_grow_in_place_zeroed
571 | .set(self.before_grow_in_place_zeroed.get() + 1)
572 | }
573 | fn after_grow_in_place_zeroed(
574 | &self,
575 | _ptr: NonNull,
576 | _old_layout: Layout,
577 | _new_layout: Layout,
578 | _result: Result,
579 | ) {
580 | self.after_grow_in_place_zeroed
581 | .set(self.after_grow_in_place_zeroed.get() + 1)
582 | }
583 | fn before_shrink(&self, _ptr: NonNull, _old_layout: Layout, _new_layout: Layout) {
584 | self.before_shrink.set(self.before_shrink.get() + 1)
585 | }
586 | fn after_shrink(
587 | &self,
588 | _ptr: NonNull,
589 | _old_layout: Layout,
590 | _new_layout: Layout,
591 | _result: Result, AllocError>,
592 | ) {
593 | self.after_shrink.set(self.after_shrink.get() + 1)
594 | }
595 | fn before_shrink_in_place(
596 | &self,
597 | _ptr: NonNull,
598 | _old_layout: Layout,
599 | _new_layout: Layout,
600 | ) {
601 | self.before_shrink_in_place
602 | .set(self.before_shrink_in_place.get() + 1)
603 | }
604 | fn after_shrink_in_place(
605 | &self,
606 | _ptr: NonNull,
607 | _old_layout: Layout,
608 | _new_layout: Layout,
609 | _result: Result,
610 | ) {
611 | self.after_shrink_in_place
612 | .set(self.after_shrink_in_place.get() + 1)
613 | }
614 | fn before_owns(&self) {
615 | self.before_owns.set(self.before_owns.get() + 1)
616 | }
617 | fn after_owns(&self, _success: bool) {
618 | self.after_owns.set(self.after_owns.get() + 1)
619 | }
620 | }
621 |
622 | fn test_callback(callback: impl CallbackRef) {
623 | callback.before_allocate(Layout::new::<()>());
624 | callback.after_allocate(Layout::new::<()>(), Err(AllocError));
625 | callback.before_allocate_zeroed(Layout::new::<()>());
626 | callback.after_allocate_zeroed(Layout::new::<()>(), Err(AllocError));
627 | callback.before_allocate_all();
628 | callback.after_allocate_all(Err(AllocError));
629 | callback.before_allocate_all_zeroed();
630 | callback.after_allocate_all_zeroed(Err(AllocError));
631 | callback.before_deallocate(NonNull::dangling(), Layout::new::<()>());
632 | callback.after_deallocate(NonNull::dangling(), Layout::new::<()>());
633 | callback.before_deallocate_all();
634 | callback.after_deallocate_all();
635 | callback.before_grow(
636 | NonNull::dangling(),
637 | Layout::new::<()>(),
638 | Layout::new::<()>(),
639 | );
640 | callback.after_grow(
641 | NonNull::dangling(),
642 | Layout::new::<()>(),
643 | Layout::new::<()>(),
644 | Err(AllocError),
645 | );
646 | callback.before_grow_zeroed(
647 | NonNull::dangling(),
648 | Layout::new::<()>(),
649 | Layout::new::<()>(),
650 | );
651 | callback.after_grow_zeroed(
652 | NonNull::dangling(),
653 | Layout::new::<()>(),
654 | Layout::new::<()>(),
655 | Err(AllocError),
656 | );
657 | callback.before_grow_in_place(
658 | NonNull::dangling(),
659 | Layout::new::<()>(),
660 | Layout::new::<()>(),
661 | );
662 | callback.after_grow_in_place(
663 | NonNull::dangling(),
664 | Layout::new::<()>(),
665 | Layout::new::<()>(),
666 | Err(AllocError),
667 | );
668 | callback.before_grow_in_place_zeroed(
669 | NonNull::dangling(),
670 | Layout::new::<()>(),
671 | Layout::new::<()>(),
672 | );
673 | callback.after_grow_in_place_zeroed(
674 | NonNull::dangling(),
675 | Layout::new::<()>(),
676 | Layout::new::<()>(),
677 | Err(AllocError),
678 | );
679 | callback.before_shrink(
680 | NonNull::dangling(),
681 | Layout::new::<()>(),
682 | Layout::new::<()>(),
683 | );
684 | callback.after_shrink(
685 | NonNull::dangling(),
686 | Layout::new::<()>(),
687 | Layout::new::<()>(),
688 | Err(AllocError),
689 | );
690 | callback.after_shrink_in_place(
691 | NonNull::dangling(),
692 | Layout::new::<()>(),
693 | Layout::new::<()>(),
694 | Err(AllocError),
695 | );
696 | callback.before_shrink_in_place(
697 | NonNull::dangling(),
698 | Layout::new::<()>(),
699 | Layout::new::<()>(),
700 | );
701 | callback.before_owns();
702 | callback.after_owns(false);
703 | }
704 |
705 | fn check_counts(callback: &Callback) {
706 | assert_eq!(callback.before_allocate.get(), 1);
707 | assert_eq!(callback.after_allocate.get(), 1);
708 | assert_eq!(callback.before_allocate_zeroed.get(), 1);
709 | assert_eq!(callback.after_allocate_zeroed.get(), 1);
710 | assert_eq!(callback.before_allocate_all.get(), 1);
711 | assert_eq!(callback.after_allocate_all.get(), 1);
712 | assert_eq!(callback.before_allocate_all_zeroed.get(), 1);
713 | assert_eq!(callback.after_allocate_all_zeroed.get(), 1);
714 | assert_eq!(callback.before_deallocate.get(), 1);
715 | assert_eq!(callback.after_deallocate.get(), 1);
716 | assert_eq!(callback.before_deallocate_all.get(), 1);
717 | assert_eq!(callback.after_deallocate_all.get(), 1);
718 | assert_eq!(callback.before_grow.get(), 1);
719 | assert_eq!(callback.after_grow.get(), 1);
720 | assert_eq!(callback.before_grow_zeroed.get(), 1);
721 | assert_eq!(callback.after_grow_zeroed.get(), 1);
722 | assert_eq!(callback.before_grow_in_place.get(), 1);
723 | assert_eq!(callback.after_grow_in_place.get(), 1);
724 | assert_eq!(callback.before_grow_in_place_zeroed.get(), 1);
725 | assert_eq!(callback.after_grow_in_place_zeroed.get(), 1);
726 | assert_eq!(callback.before_shrink.get(), 1);
727 | assert_eq!(callback.after_shrink.get(), 1);
728 | assert_eq!(callback.before_shrink_in_place.get(), 1);
729 | assert_eq!(callback.after_shrink_in_place.get(), 1);
730 | assert_eq!(callback.before_owns.get(), 1);
731 | assert_eq!(callback.after_owns.get(), 1);
732 | }
733 |
734 | #[test]
735 | fn plain() {
736 | let callback = Callback::default();
737 | test_callback(callback.by_ref());
738 | check_counts(&callback);
739 | }
740 |
741 | #[test]
742 | fn boxed() {
743 | let callback = Box::new(Callback::default());
744 | test_callback(callback.by_ref());
745 | check_counts(&callback);
746 | }
747 |
748 | #[test]
749 | fn rc() {
750 | let callback = Rc::new(Callback::default());
751 | test_callback(callback.by_ref());
752 | check_counts(&callback);
753 | }
754 |
755 | #[test]
756 | fn arc() {
757 | let callback = Arc::new(Callback::default());
758 | test_callback(callback.by_ref());
759 | check_counts(&callback);
760 | }
761 | }
762 |
--------------------------------------------------------------------------------
/src/chunk.rs:
--------------------------------------------------------------------------------
1 | use crate::{helper::AllocInit, Owns, ReallocateInPlace};
2 | use core::{
3 | alloc::{AllocError, AllocRef, Layout},
4 | ptr::NonNull,
5 | };
6 |
7 | /// Allocate memory with a multiple size of the provided chunk size.
8 | ///
9 | /// # Examples
10 | ///
11 | /// ```rust
12 | /// #![feature(allocator_api, slice_ptr_len)]
13 | ///
14 | /// use alloc_compose::Chunk;
15 | /// use std::alloc::{AllocRef, Layout, System};
16 | ///
17 | /// let mut alloc = Chunk::<_, 64>(System);
18 | /// let ptr = alloc.alloc(Layout::new::<[u8; 16]>())?;
19 | /// assert_eq!(ptr.len() % 64, 0);
20 | /// assert!(ptr.len() >= 64);
21 | /// # Ok::<(), core::alloc::AllocError>(())
22 | /// ```
23 | ///
24 | /// When growing or shrinking the memory, `Chunk` will try to alter
25 | /// the memory in place before delegating to the underlying allocator.
26 | ///
27 | /// ```rust
28 | /// #![feature(slice_ptr_get)]
29 | /// # #![feature(allocator_api, slice_ptr_len)]
30 | /// # use alloc_compose::Chunk;
31 | /// # use std::{alloc::{AllocRef, Layout, System}};
32 | /// # let mut alloc = Chunk::<_, 64>(System);
33 | /// # let ptr = alloc.alloc(Layout::new::<[u8; 16]>())?;
34 | ///
35 | /// let new_ptr = unsafe {
36 | /// alloc.grow(
37 | /// ptr.as_non_null_ptr(),
38 | /// Layout::new::<[u8; 16]>(),
39 | /// Layout::new::<[u8; 24]>(),
40 | /// )?
41 | /// };
42 | ///
43 | /// assert_eq!(ptr, new_ptr);
44 | /// # Ok::<(), core::alloc::AllocError>(())
45 | /// ```
46 | ///
47 | /// This can be enforced by using [`ReallocateInPlace::grow_in_place`].
48 | ///
49 | /// ```rust
50 | /// # #![feature(allocator_api, slice_ptr_len, slice_ptr_get)]
51 | /// # use alloc_compose::Chunk;
52 | /// # use std::{alloc::{AllocRef, Layout, System}};
53 | /// # let mut alloc = Chunk::<_, 64>(System);
54 | /// # let ptr = alloc.alloc(Layout::new::<[u8; 24]>())?;
55 | /// use alloc_compose::ReallocateInPlace;
56 | ///
57 | /// let len = unsafe {
58 | /// alloc.grow_in_place(
59 | /// ptr.as_non_null_ptr(),
60 | /// Layout::new::<[u8; 24]>(),
61 | /// Layout::new::<[u8; 32]>(),
62 | /// )?
63 | /// };
64 | ///
65 | /// assert_eq!(len % 64, 0);
66 | /// assert!(len >= 64);
67 | /// # Ok::<(), core::alloc::AllocError>(())
68 | /// ```
69 | #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
70 | pub struct Chunk(pub A);
71 |
72 | mod sealed {
73 | pub trait SizeIsPowerOfTwo {}
74 | }
75 | use sealed::SizeIsPowerOfTwo;
76 |
77 | macro_rules! is_power_of_two {
78 | ($($N:literal)+) => {
79 | $(
80 | impl SizeIsPowerOfTwo for Chunk {}
81 | )+
82 | };
83 | }
84 |
85 | is_power_of_two!(1 2 3 4 5 6 7);
86 | #[cfg(any(
87 | target_pointer_width = "16",
88 | target_pointer_width = "32",
89 | target_pointer_width = "64"
90 | ))]
91 | is_power_of_two!(8 9 10 11 12 13 14 15);
92 | #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
93 | is_power_of_two!(16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31);
94 | #[cfg(target_pointer_width = "64")]
95 | is_power_of_two!(32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63);
96 |
97 | impl Chunk
98 | where
99 | Self: SizeIsPowerOfTwo,
100 | {
101 | fn round_up(size: usize) -> Result {
102 | Ok((size.checked_add(SIZE).ok_or(AllocError)? - 1) & !(SIZE - 1))
103 | }
104 |
105 | unsafe fn round_up_unchecked(size: usize) -> usize {
106 | let new_size = (size.wrapping_add(SIZE) - 1) & !(SIZE - 1);
107 | debug_assert_eq!(new_size, Self::round_up(size).unwrap());
108 | new_size
109 | }
110 |
111 | const fn round_down(size: usize) -> usize {
112 | size & !(SIZE - 1)
113 | }
114 |
115 | const fn round_down_ptr_len(ptr: NonNull<[u8]>) -> NonNull<[u8]> {
116 | NonNull::slice_from_raw_parts(ptr.as_non_null_ptr(), Self::round_down(ptr.len()))
117 | }
118 |
119 | #[inline]
120 | fn alloc_impl(
121 | layout: Layout,
122 | alloc: impl FnOnce(Layout) -> Result, AllocError>,
123 | ) -> Result, AllocError> {
124 | let new_size = Self::round_up(layout.size())?;
125 | let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
126 |
127 | alloc(new_layout).map(Self::round_down_ptr_len)
128 | }
129 |
130 | #[inline]
131 | unsafe fn grow_impl(
132 | old_ptr: NonNull,
133 | old_layout: Layout,
134 | new_layout: Layout,
135 | init: AllocInit,
136 | grow: impl FnOnce(NonNull, Layout, Layout) -> Result, AllocError>,
137 | ) -> Result, AllocError> {
138 | let old_size = old_layout.size();
139 | let current_size = Self::round_up_unchecked(old_size);
140 | let new_size = new_layout.size();
141 | if new_layout.align() <= old_layout.align() && new_size <= current_size {
142 | let ptr = NonNull::slice_from_raw_parts(old_ptr, current_size);
143 | init.init_offset(ptr, old_size);
144 | return Ok(ptr);
145 | }
146 |
147 | grow(
148 | old_ptr,
149 | Layout::from_size_align_unchecked(current_size, old_layout.align()),
150 | Layout::from_size_align_unchecked(Self::round_up(new_size)?, new_layout.align()),
151 | )
152 | .map(Self::round_down_ptr_len)
153 | }
154 |
155 | #[inline]
156 | unsafe fn shrink_impl(
157 | old_ptr: NonNull,
158 | old_layout: Layout,
159 | new_layout: Layout,
160 | shrink: impl FnOnce(NonNull, Layout, Layout) -> Result, AllocError>,
161 | ) -> Result, AllocError> {
162 | let current_size = Self::round_up_unchecked(old_layout.size());
163 | let new_size = new_layout.size();
164 | if new_layout.align() <= old_layout.align() && new_layout.size() > current_size - SIZE {
165 | return Ok(NonNull::slice_from_raw_parts(old_ptr, current_size));
166 | }
167 |
168 | shrink(
169 | old_ptr,
170 | old_layout,
171 | Layout::from_size_align_unchecked(
172 | Self::round_up_unchecked(new_size),
173 | new_layout.align(),
174 | ),
175 | )
176 | .map(Self::round_down_ptr_len)
177 | }
178 | }
179 |
180 | unsafe impl AllocRef for Chunk
181 | where
182 | Self: SizeIsPowerOfTwo,
183 | {
184 | impl_alloc_ref!(0);
185 |
186 | unsafe fn dealloc(&self, ptr: NonNull, layout: Layout) {
187 | crate::check_dealloc_precondition(ptr, layout);
188 |
189 | self.0.dealloc(
190 | ptr,
191 | Layout::from_size_align_unchecked(
192 | Self::round_up_unchecked(layout.size()),
193 | layout.align(),
194 | ),
195 | )
196 | }
197 | }
198 |
199 | // unsafe impl AllocateAll for Chunk
200 | // where
201 | // Self: SizeIsPowerOfTwo,
202 | // {
203 | // impl_alloc_all!(0);
204 | // }
205 |
206 | unsafe impl ReallocateInPlace for Chunk
207 | where
208 | Self: SizeIsPowerOfTwo,
209 | {
210 | impl_realloc_in_place_spec!(0);
211 | }
212 |
213 | unsafe impl ReallocateInPlace for Chunk
214 | where
215 | Self: SizeIsPowerOfTwo,
216 | {
217 | impl_realloc_in_place!(0);
218 | }
219 |
220 | impl Owns for Chunk
221 | where
222 | Self: SizeIsPowerOfTwo,
223 | {
224 | fn owns(&self, memory: NonNull<[u8]>) -> bool {
225 | self.0.owns(memory)
226 | }
227 | }
228 |
229 | #[cfg(test)]
230 | mod tests {
231 | use super::Chunk;
232 | use crate::{helper::tracker, ReallocateInPlace};
233 | use alloc::alloc::Global;
234 | use core::alloc::{AllocRef, Layout};
235 |
236 | #[test]
237 | fn alloc() {
238 | let alloc = Chunk::<_, 64>(tracker(Global));
239 | let memory = alloc
240 | .alloc(Layout::new::<[u8; 2]>())
241 | .expect("Could not allocate 64 bytes");
242 | assert_eq!(memory.len() % 64, 0);
243 | assert!(memory.len() >= 64);
244 |
245 | unsafe {
246 | alloc.dealloc(memory.as_non_null_ptr(), Layout::new::());
247 | }
248 | }
249 |
250 | #[test]
251 | fn dealloc() {
252 | let alloc = Chunk::<_, 64>(tracker(Global));
253 |
254 | unsafe {
255 | let memory = alloc
256 | .alloc(Layout::new::<[u8; 4]>())
257 | .expect("Could not allocate 4 bytes");
258 | assert_eq!(memory.len() % 64, 0);
259 | alloc.dealloc(memory.as_non_null_ptr(), Layout::new::<[u8; 4]>());
260 |
261 | let memory = alloc
262 | .alloc(Layout::new::<[u8; 8]>())
263 | .expect("Could not allocate 8 bytes");
264 | assert_eq!(memory.len() % 64, 0);
265 | alloc.dealloc(memory.as_non_null_ptr(), Layout::new::<[u8; 8]>());
266 |
267 | let memory = alloc
268 | .alloc(Layout::new::<[u8; 32]>())
269 | .expect("Could not allocate 32 bytes");
270 | assert_eq!(memory.len() % 64, 0);
271 | alloc.dealloc(memory.as_non_null_ptr(), Layout::new::<[u8; 32]>());
272 |
273 | let memory = alloc
274 | .alloc(Layout::new::<[u8; 64]>())
275 | .expect("Could not allocate 64 bytes");
276 | assert_eq!(memory.len() % 64, 0);
277 | alloc.dealloc(memory.as_non_null_ptr(), Layout::new::<[u8; 64]>());
278 | }
279 | }
280 |
281 | #[test]
282 | fn grow() {
283 | let alloc = Chunk::<_, 64>(tracker(Global));
284 |
285 | let memory = alloc
286 | .alloc(Layout::new::<[u8; 4]>())
287 | .expect("Could not allocate 4 bytes");
288 | assert_eq!(memory.len() % 64, 0);
289 |
290 | unsafe {
291 | let len = alloc
292 | .grow_in_place(
293 | memory.as_non_null_ptr(),
294 | Layout::new::<[u8; 4]>(),
295 | Layout::new::<[u8; 64]>(),
296 | )
297 | .expect("Could not grow to 8 bytes");
298 | assert_eq!(len % 64, 0);
299 | assert!(len >= 64);
300 |
301 | let len = alloc
302 | .grow_in_place(
303 | memory.as_non_null_ptr(),
304 | Layout::new::<[u8; 8]>(),
305 | Layout::new::<[u8; 64]>(),
306 | )
307 | .expect("Could not grow to 64 bytes");
308 | assert_eq!(len % 64, 0);
309 | assert!(len >= 64);
310 |
311 | alloc
312 | .grow_in_place(
313 | memory.as_non_null_ptr(),
314 | Layout::new::<[u8; 64]>(),
315 | Layout::new::<[u8; 65]>(),
316 | )
317 | .expect_err("Could grow to 65 bytes in place");
318 |
319 | let memory = alloc
320 | .grow(
321 | memory.as_non_null_ptr(),
322 | Layout::new::<[u8; 64]>(),
323 | Layout::new::<[u8; 65]>(),
324 | )
325 | .expect("Could not grow to 65 bytes");
326 |
327 | alloc.dealloc(memory.as_non_null_ptr(), Layout::new::<[u8; 65]>());
328 | }
329 | }
330 |
331 | #[test]
332 | fn shrink() {
333 | let alloc = Chunk::<_, 64>(tracker(Global));
334 |
335 | let memory = alloc
336 | .alloc(Layout::new::<[u8; 128]>())
337 | .expect("Could not allocate 128 bytes");
338 | assert_eq!(memory.len() % 64, 0);
339 |
340 | unsafe {
341 | let len = alloc
342 | .shrink_in_place(
343 | memory.as_non_null_ptr(),
344 | Layout::new::<[u8; 128]>(),
345 | Layout::new::<[u8; 100]>(),
346 | )
347 | .expect("Could not shrink to 100 bytes");
348 | assert_eq!(len % 64, 0);
349 | assert!(len >= 128);
350 |
351 | let len = alloc
352 | .shrink_in_place(
353 | memory.as_non_null_ptr(),
354 | Layout::new::<[u8; 100]>(),
355 | Layout::new::<[u8; 65]>(),
356 | )
357 | .expect("Could not shrink to 65 bytes");
358 | assert_eq!(len % 64, 0);
359 | assert!(len >= 128);
360 |
361 | alloc
362 | .shrink_in_place(
363 | memory.as_non_null_ptr(),
364 | Layout::new::<[u8; 65]>(),
365 | Layout::new::<[u8; 64]>(),
366 | )
367 | .expect_err("Could shrink to 64 bytes in place");
368 |
369 | let memory = alloc
370 | .shrink(
371 | memory.as_non_null_ptr(),
372 | Layout::new::<[u8; 128]>(),
373 | Layout::new::<[u8; 64]>(),
374 | )
375 | .expect("Could not shrink to 64 bytes");
376 |
377 | alloc.dealloc(memory.as_non_null_ptr(), Layout::new::<[u8; 64]>());
378 | }
379 | }
380 | }
381 |
--------------------------------------------------------------------------------
/src/fallback.rs:
--------------------------------------------------------------------------------
1 | use crate::{
2 | helper::{grow_fallback, AllocInit},
3 | Owns,
4 | };
5 | use core::{
6 | alloc::{AllocError, AllocRef, Layout},
7 | ptr::NonNull,
8 | };
9 |
10 | /// An allocator equivalent of an "or" operator in algebra.
11 | ///
12 | /// An allocation request is first attempted with the `Primary` allocator. If that fails, the
13 | /// request is forwarded to the `Fallback` allocator. All other requests are dispatched
14 | /// appropriately to one of the two allocators.
15 | ///
16 | /// A `Fallback` is useful for fast, special-purpose allocators backed up by general-purpose
17 | /// allocators like [`Global`] or [`System`].
18 | ///
19 | /// [`Global`]: https://doc.rust-lang.org/alloc/alloc/struct.Global.html
20 | /// [`System`]: https://doc.rust-lang.org/std/alloc/struct.System.html
21 | ///
22 | /// # Example
23 | ///
24 | /// ```rust
25 | /// #![feature(allocator_api, slice_ptr_get)]
26 | ///
27 | /// use alloc_compose::{region::Region, Fallback, Owns};
28 | /// use std::{
29 | /// alloc::{AllocRef, Layout, System},
30 | /// mem::MaybeUninit,
31 | /// };
32 | ///
33 | /// let mut data = [MaybeUninit::new(0); 32];
34 | /// let mut alloc = Fallback {
35 | /// primary: Region::new(&mut data),
36 | /// secondary: System,
37 | /// };
38 | ///
39 | /// let small_memory = alloc.alloc(Layout::new::())?;
40 | /// let big_memory = alloc.alloc(Layout::new::<[u32; 64]>())?;
41 | ///
42 | /// assert!(alloc.primary.owns(small_memory));
43 | /// assert!(!alloc.primary.owns(big_memory));
44 | ///
45 | /// unsafe {
46 | /// // `big_memory` was allocated from `System`, we can dealloc it directly
47 | /// System.dealloc(big_memory.as_non_null_ptr(), Layout::new::<[u32; 64]>());
48 | /// alloc.dealloc(small_memory.as_non_null_ptr(), Layout::new::());
49 | /// };
50 | /// # Ok::<(), core::alloc::AllocError>(())
51 | /// ```
52 | #[derive(Debug, Copy, Clone)]
53 | pub struct Fallback {
54 | /// The primary allocator
55 | pub primary: Primary,
56 | /// The fallback allocator
57 | pub secondary: Secondary,
58 | }
59 |
60 | unsafe impl AllocRef for Fallback
61 | where
62 | Primary: AllocRef + Owns,
63 | Secondary: AllocRef,
64 | {
65 | fn alloc(&self, layout: Layout) -> Result, AllocError> {
66 | match self.primary.alloc(layout) {
67 | primary @ Ok(_) => primary,
68 | Err(_) => self.secondary.alloc(layout),
69 | }
70 | }
71 |
72 | fn alloc_zeroed(&self, layout: Layout) -> Result, AllocError> {
73 | match self.primary.alloc_zeroed(layout) {
74 | primary @ Ok(_) => primary,
75 | Err(_) => self.secondary.alloc_zeroed(layout),
76 | }
77 | }
78 |
79 | unsafe fn dealloc(&self, ptr: NonNull, layout: Layout) {
80 | if self
81 | .primary
82 | .owns(NonNull::slice_from_raw_parts(ptr, layout.size()))
83 | {
84 | self.primary.dealloc(ptr, layout)
85 | } else {
86 | self.secondary.dealloc(ptr, layout)
87 | }
88 | }
89 |
90 | unsafe fn grow(
91 | &self,
92 | ptr: NonNull,
93 | old_layout: Layout,
94 | new_layout: Layout,
95 | ) -> Result, AllocError> {
96 | if self
97 | .primary
98 | .owns(NonNull::slice_from_raw_parts(ptr, old_layout.size()))
99 | {
100 | if let Ok(memory) = self.primary.grow(ptr, old_layout, new_layout) {
101 | Ok(memory)
102 | } else {
103 | grow_fallback(
104 | &self.primary,
105 | &self.secondary,
106 | ptr,
107 | old_layout,
108 | new_layout,
109 | AllocInit::Uninitialized,
110 | )
111 | }
112 | } else {
113 | self.secondary.grow(ptr, old_layout, new_layout)
114 | }
115 | }
116 |
117 | unsafe fn grow_zeroed(
118 | &self,
119 | ptr: NonNull,
120 | old_layout: Layout,
121 | new_layout: Layout,
122 | ) -> Result, AllocError> {
123 | if self
124 | .primary
125 | .owns(NonNull::slice_from_raw_parts(ptr, old_layout.size()))
126 | {
127 | if let Ok(memory) = self.primary.grow_zeroed(ptr, old_layout, new_layout) {
128 | Ok(memory)
129 | } else {
130 | grow_fallback(
131 | &self.primary,
132 | &self.secondary,
133 | ptr,
134 | old_layout,
135 | new_layout,
136 | AllocInit::Zeroed,
137 | )
138 | }
139 | } else {
140 | self.secondary.grow_zeroed(ptr, old_layout, new_layout)
141 | }
142 | }
143 |
144 | unsafe fn shrink(
145 | &self,
146 | ptr: NonNull,
147 | old_layout: Layout,
148 | new_layout: Layout,
149 | ) -> Result, AllocError> {
150 | if self
151 | .primary
152 | .owns(NonNull::slice_from_raw_parts(ptr, old_layout.size()))
153 | {
154 | self.primary.shrink(ptr, old_layout, new_layout)
155 | } else {
156 | self.secondary.shrink(ptr, old_layout, new_layout)
157 | }
158 | }
159 | }
160 |
161 | impl Owns for Fallback
162 | where
163 | Primary: Owns,
164 | Secondary: Owns,
165 | {
166 | fn owns(&self, memory: NonNull<[u8]>) -> bool {
167 | self.primary.owns(memory) || self.secondary.owns(memory)
168 | }
169 | }
170 |
171 | #[cfg(test)]
172 | mod tests {
173 | use super::Fallback;
174 | use crate::{helper, region::Region, Chunk, Owns};
175 | use alloc::alloc::Global;
176 | use core::{
177 | alloc::{AllocRef, Layout},
178 | mem::MaybeUninit,
179 | };
180 |
181 | #[test]
182 | fn alloc() {
183 | let mut data = [MaybeUninit::new(0); 32];
184 | let alloc = Fallback {
185 | primary: helper::tracker(Region::new(&mut data)),
186 | secondary: helper::tracker(Global),
187 | };
188 |
189 | let small_memory = alloc
190 | .alloc(Layout::new::())
191 | .expect("Could not allocate 4 bytes");
192 | let big_memory = alloc
193 | .alloc(Layout::new::<[u8; 64]>())
194 | .expect("Could not allocate 64 bytes");
195 |
196 | assert!(alloc.primary.owns(small_memory));
197 | assert!(!alloc.primary.owns(big_memory));
198 | unsafe {
199 | alloc.dealloc(small_memory.as_non_null_ptr(), Layout::new::());
200 | alloc.dealloc(big_memory.as_non_null_ptr(), Layout::new::<[u8; 64]>());
201 | };
202 | }
203 |
204 | #[test]
205 | fn grow() {
206 | let mut data = [MaybeUninit::new(0); 80];
207 | let alloc = Fallback {
208 | primary: helper::tracker(Chunk::(Region::new(&mut data))),
209 | secondary: helper::tracker(Global),
210 | };
211 |
212 | let memory = alloc
213 | .alloc(Layout::new::<[u8; 32]>())
214 | .expect("Could not allocate 4 bytes");
215 | assert!(alloc.primary.owns(memory));
216 |
217 | unsafe {
218 | let memory = alloc
219 | .grow(
220 | memory.as_non_null_ptr(),
221 | Layout::new::<[u8; 32]>(),
222 | Layout::new::<[u8; 64]>(),
223 | )
224 | .expect("Could not grow to 64 bytes");
225 | assert!(alloc.primary.owns(memory));
226 | assert_eq!(memory.len(), 64);
227 |
228 | let memory = alloc
229 | .grow(
230 | memory.as_non_null_ptr(),
231 | Layout::new::<[u8; 64]>(),
232 | Layout::new::<[u8; 128]>(),
233 | )
234 | .expect("Could not grow to 128 bytes");
235 | assert!(!alloc.primary.owns(memory));
236 |
237 | alloc.dealloc(memory.as_non_null_ptr(), Layout::new::<[u8; 128]>());
238 | };
239 | }
240 |
241 | #[test]
242 | fn shrink() {
243 | let mut data = [MaybeUninit::new(0); 80];
244 | let alloc = Fallback {
245 | primary: helper::tracker(Chunk::(Region::new(&mut data))),
246 | secondary: helper::tracker(Global),
247 | };
248 |
249 | let memory = alloc
250 | .alloc(Layout::new::<[u8; 64]>())
251 | .expect("Could not allocate 64 bytes");
252 | assert!(alloc.primary.owns(memory));
253 |
254 | unsafe {
255 | let memory = alloc
256 | .shrink(
257 | memory.as_non_null_ptr(),
258 | Layout::new::<[u8; 64]>(),
259 | Layout::new::<[u8; 32]>(),
260 | )
261 | .expect("Could not shrink to 32 bytes");
262 | assert!(alloc.primary.owns(memory));
263 |
264 | let memory = alloc
265 | .grow(
266 | memory.as_non_null_ptr(),
267 | Layout::new::<[u8; 32]>(),
268 | Layout::new::<[u8; 128]>(),
269 | )
270 | .expect("Could not grow to 128 bytes");
271 | assert!(!alloc.primary.owns(memory));
272 |
273 | let memory = alloc
274 | .shrink(
275 | memory.as_non_null_ptr(),
276 | Layout::new::<[u8; 128]>(),
277 | Layout::new::<[u8; 96]>(),
278 | )
279 | .expect("Could not shrink to 96 bytes");
280 | assert!(!alloc.primary.owns(memory));
281 |
282 | alloc.dealloc(memory.as_non_null_ptr(), Layout::new::<[u8; 96]>());
283 | }
284 | }
285 |
286 | #[test]
287 | fn owns() {
288 | let mut data_1 = [MaybeUninit::new(0); 32];
289 | let mut data_2 = [MaybeUninit::new(0); 64];
290 | let alloc = Fallback {
291 | primary: Region::new(&mut data_1),
292 | secondary: Region::new(&mut data_2),
293 | };
294 |
295 | let memory = alloc
296 | .alloc(Layout::new::<[u8; 32]>())
297 | .expect("Could not allocate 32 bytes");
298 | assert!(alloc.primary.owns(memory));
299 | assert!(alloc.owns(memory));
300 |
301 | let memory = alloc
302 | .alloc(Layout::new::<[u8; 64]>())
303 | .expect("Could not allocate 64 bytes");
304 | assert!(alloc.secondary.owns(memory));
305 | assert!(alloc.owns(memory));
306 | }
307 | }
308 |
--------------------------------------------------------------------------------
/src/helper.rs:
--------------------------------------------------------------------------------
1 | use core::{
2 | alloc::{AllocError, AllocRef, Layout},
3 | ptr::{self, NonNull},
4 | };
5 |
6 | #[derive(Copy, Clone, PartialEq, Eq)]
7 | pub enum AllocInit {
8 | Uninitialized,
9 | Zeroed,
10 | }
11 |
12 | impl AllocInit {
13 | #[inline]
14 | pub unsafe fn init_offset(self, ptr: NonNull<[u8]>, offset: usize) {
15 | debug_assert!(
16 | offset <= ptr.len(),
17 | "`offset` must be smaller than or equal to `ptr.len()`"
18 | );
19 | match self {
20 | Self::Uninitialized => (),
21 | Self::Zeroed => ptr
22 | .as_non_null_ptr()
23 | .as_ptr()
24 | .add(offset)
25 | .write_bytes(0, ptr.len() - offset),
26 | }
27 | }
28 | }
29 |
30 | // #[derive(Copy, Clone, PartialEq, Eq)]
31 | // pub enum ReallocPlacement {
32 | // MayMove,
33 | // InPlace,
34 | // }
35 |
36 | pub(in crate) unsafe fn grow_fallback(
37 | a1: &A1,
38 | a2: &A2,
39 | ptr: NonNull,
40 | old_layout: Layout,
41 | new_layout: Layout,
42 | init: AllocInit,
43 | ) -> Result, AllocError> {
44 | let new_ptr = match init {
45 | AllocInit::Uninitialized => a2.alloc(new_layout)?,
46 | AllocInit::Zeroed => a2.alloc_zeroed(new_layout)?,
47 | };
48 | ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_layout.size());
49 | a1.dealloc(ptr, old_layout);
50 | Ok(new_ptr)
51 | }
52 |
53 | pub(in crate) unsafe fn shrink_fallback(
54 | a1: &A1,
55 | a2: &A2,
56 | ptr: NonNull,
57 | old_layout: Layout,
58 | new_layout: Layout,
59 | ) -> Result, AllocError> {
60 | let new_ptr = a2.alloc(new_layout)?;
61 | ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_layout.size());
62 | a1.dealloc(ptr, old_layout);
63 | Ok(new_ptr)
64 | }
65 |
66 | #[cfg(test)]
67 | pub fn tracker(alloc: A) -> crate::Proxy {
68 | crate::Proxy {
69 | alloc,
70 | callbacks: self::tests::Tracker::default(),
71 | }
72 | }
73 |
74 | #[cfg(test)]
75 | mod tests {
76 | use super::tracker;
77 | use crate::{CallbackRef, Chunk};
78 | use alloc::{alloc::Global, collections::BTreeMap};
79 | use core::{
80 | alloc::{AllocError, AllocRef, Layout},
81 | cell::RefCell,
82 | ptr::NonNull,
83 | };
84 |
85 | #[cfg(test)]
86 | #[derive(Default)]
87 | pub struct Tracker {
88 | map: RefCell, (usize, Layout)>>,
89 | }
90 |
91 | impl Tracker {
92 | #[track_caller]
93 | fn assert_fit_memory(&self, ptr: NonNull, layout: Layout, name: &str) {
94 | let map = self.map.borrow();
95 | let (size, old_layout) = map.get(&ptr).expect(
96 | "`ptr` must denote a block of memory currently allocated via this allocator",
97 | );
98 | assert_eq!(
99 | layout.align(),
100 | old_layout.align(),
101 | "`{0}` must fit that block of memory. The block must be allocated with the same \
102 | alignment as `{1}.align()`. Expected alignment of {1}, got {2}",
103 | name,
104 | old_layout.align(),
105 | layout.align()
106 | );
107 | if *size == old_layout.size() {
108 | assert_eq!(
109 | layout.size(),
110 | old_layout.size(),
111 | "`{0}` must fit that block of memory. The provided `{0}.size()` must fall in \
112 | the range `min ..= max`. Expected size of {1}, got {2}",
113 | name,
114 | old_layout.size(),
115 | layout.size()
116 | )
117 | } else {
118 | assert!(
119 | layout.size() <= *size && layout.size() >= old_layout.size(),
120 | "`{0}` must fit that block of memory. The provided `{0}.size()` must fall in \
121 | the range `min ..= max`. Expected size between `{1} ..= {2}`, got {3}",
122 | name,
123 | old_layout.size(),
124 | size,
125 | layout.size()
126 | )
127 | }
128 | }
129 | }
130 |
131 | #[cfg(test)]
132 | unsafe impl CallbackRef for Tracker {
133 | fn after_allocate(&self, layout: Layout, result: Result, AllocError>) {
134 | if let Ok(ptr) = result {
135 | self.map
136 | .borrow_mut()
137 | .insert(ptr.as_non_null_ptr(), (ptr.len(), layout));
138 | }
139 | }
140 |
141 | fn after_allocate_zeroed(&self, layout: Layout, result: Result, AllocError>) {
142 | self.after_allocate(layout, result)
143 | }
144 |
145 | fn after_allocate_all(&self, result: Result, AllocError>) {
146 | if let Ok(ptr) = result {
147 | let layout =
148 | Layout::from_size_align(ptr.len(), 1).expect("Invalid layout for allocate_all");
149 | self.after_allocate(layout, result);
150 | }
151 | }
152 |
153 | fn after_allocate_all_zeroed(&self, result: Result, AllocError>) {
154 | self.after_allocate_all(result)
155 | }
156 |
157 | #[track_caller]
158 | fn before_deallocate(&self, ptr: NonNull, layout: Layout) {
159 | self.assert_fit_memory(ptr, layout, "layout");
160 | }
161 |
162 | fn after_deallocate(&self, ptr: NonNull, _layout: Layout) {
163 | let mut map = self.map.borrow_mut();
164 | map.remove(&ptr);
165 | }
166 |
167 | fn after_deallocate_all(&self) {
168 | let mut map = self.map.borrow_mut();
169 | map.clear()
170 | }
171 |
172 | #[track_caller]
173 | fn before_grow(&self, ptr: NonNull