├── .github └── workflows │ ├── ci.yml │ └── fuzz.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── Cargo.toml ├── LICENCE.md ├── README.md ├── benches └── pool.rs ├── fuzz ├── .gitignore ├── Cargo.toml └── fuzz_targets │ └── refpool.rs ├── release.toml └── src ├── box_handle.rs ├── counter.rs ├── fakepool.rs ├── lib.rs ├── pointer.rs ├── pool.rs ├── ref_handle.rs ├── refbox.rs ├── stack.rs ├── std_types.rs └── types.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | on: 3 | push: 4 | pull_request: 5 | schedule: 6 | - cron: "0 0 1,15 * *" 7 | 8 | jobs: 9 | check: 10 | name: Check 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | rust: 15 | - stable 16 | - nightly 17 | - 1.43.0 # lowest supported version 18 | steps: 19 | - uses: actions/checkout@v2 20 | - uses: actions-rs/toolchain@v1 21 | with: 22 | profile: minimal 23 | toolchain: ${{ matrix.rust }} 24 | override: true 25 | - uses: actions-rs/cargo@v1 26 | with: 27 | command: check 28 | 29 | test: 30 | name: Tests 31 | runs-on: ubuntu-latest 32 | strategy: 33 | matrix: 34 | rust: 35 | - stable 36 | - 1.43.0 # lowest supported version 37 | steps: 38 | - uses: actions/checkout@v2 39 | - uses: actions-rs/toolchain@v1 40 | with: 41 | profile: minimal 42 | toolchain: ${{ matrix.rust }} 43 | override: true 44 | - uses: actions-rs/cargo@v1 45 | with: 46 | command: test 47 | 48 | test-nightly: 49 | name: Tests --all-features (nightly) 50 | runs-on: ubuntu-latest 51 | steps: 52 | - uses: actions/checkout@v2 53 | - uses: actions-rs/toolchain@v1 54 | with: 55 | profile: minimal 56 | toolchain: nightly 57 | override: true 58 | - uses: actions-rs/cargo@v1 59 | with: 60 | command: test 61 | args: --all-features 62 | 63 | fmt: 64 | name: Rustfmt 65 | runs-on: ubuntu-latest 66 | steps: 67 | - uses: actions/checkout@v2 68 | - uses: actions-rs/toolchain@v1 69 | with: 70 | profile: minimal 71 | toolchain: stable 72 | override: true 73 | components: rustfmt 74 | - uses: actions-rs/cargo@v1 75 | with: 76 | command: fmt 77 | args: --all -- --check 78 | 79 | clippy: 80 | name: Clippy 81 | runs-on: ubuntu-latest 82 | steps: 83 | - uses: actions/checkout@v2 84 | - uses: actions-rs/toolchain@v1 85 | with: 86 | profile: minimal 87 | toolchain: stable 88 | override: true 89 | components: clippy 90 | - uses: actions-rs/clippy-check@v1 91 | with: 92 | name: Clippy-${{ matrix.rust }} 93 | token: ${{ secrets.GITHUB_TOKEN }} 94 | -------------------------------------------------------------------------------- /.github/workflows/fuzz.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: libFuzzer 4 | 5 | jobs: 6 | fuzz: 7 | name: libFuzzer 8 | runs-on: ubuntu-latest 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | target: 13 | - refpool 14 | steps: 15 | - uses: actions/checkout@v2 16 | name: Checkout project 17 | - uses: actions/cache@v1 18 | name: Cache corpus 19 | id: cache-corpus 20 | with: 21 | path: fuzz/corpus/${{ matrix.target }} 22 | key: fuzz-corpus-${{ matrix.target }}-${{ github.run_id }} 23 | restore-keys: | 24 | fuzz-corpus-${{ matrix.target }}- 25 | - uses: actions-rs/toolchain@v1 26 | name: Install Rust 27 | with: 28 | profile: minimal 29 | toolchain: nightly 30 | override: true 31 | - uses: actions-rs/install@v0.1 32 | name: Install cargo-fuzz 33 | with: 34 | crate: cargo-fuzz 35 | version: latest 36 | use-tool-cache: true 37 | - name: Fuzz for 10 minutes 38 | run: cargo fuzz run ${{ matrix.target }} -- -max_total_time=600 # seconds 39 | - uses: actions/upload-artifact@v1 40 | name: Publish artifacts 41 | with: 42 | name: fuzz-artifacts 43 | path: fuzz/artifacts 44 | - uses: actions/upload-artifact@v2 45 | name: Publish corpus 46 | with: 47 | name: fuzz-corpus 48 | path: fuzz/corpus 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | .vscode/ 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project 6 | adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.4.3] - 2020-08-09 9 | 10 | ### ADDED 11 | 12 | - `Pool` has a new method `filled()` which calls `fill()` on an owned pool and passes ownership 13 | back, for constructing filled pools with a one liner: `Pool::new(31337).filled()`. 14 | 15 | ## [0.4.2] - 2020-04-24 16 | 17 | ### ADDED 18 | 19 | - `PoolClone` now also doesn't erroneously require `Default` when using the `default_impl` flag. 20 | 21 | ## [0.4.1] - 2020-04-24 22 | 23 | ### ADDED 24 | 25 | - The requirement for `PoolClone` implementors to also implement `PoolDefault` has been removed. 26 | 27 | ## [0.4.0] - 2020-04-24 28 | 29 | ### CHANGED 30 | 31 | - `PoolRef::cloned` has been made an associated function, to avoid conflicts with wrapped methods. 32 | Where you'd previously do `poolref.cloned(pool)` you'll now have to do 33 | `PoolRef::cloned(pool, poolref)`. 34 | 35 | ### ADDED 36 | 37 | - There's now a feature flag `default_impl` which removes the `PoolDefaultImpl` trait and instead 38 | uses specialisation (specifically the `min_specialization` language feature) to provide default 39 | implementations for `PoolClone` and `PoolDefault` for any type implementing `Clone` and 40 | `Default`. As this needs an unstable language feature to be enabled, it will only work on 41 | nightly rustc. 42 | - `PoolBox` and `PoolRef` now have `into_raw` and `from_raw` functions, plus `into_raw_non_null` 43 | for `PoolBox`, which work similarly to their `Box` and `Rc` counterparts. To accommodate this, 44 | the memory layout of the internal `RefBox` structure has changed, so that the pointer stored in 45 | a `PoolBox` or `PoolRef` is now guaranteed to point at the boxed value. 46 | - There is now a `refpool::fakepool` module which provides fake versions of `Pool`, `PoolBox` and 47 | `PoolRef`, the latter two being zero cost wrappers around `Box` and `Rc`, and `Pool` being a 48 | zero sized structure that will optimise away entirely, for situations where you may want to only 49 | optionally use the pool but want to write the same code for both situations. 50 | 51 | ## [0.3.1] - 2020-04-23 52 | 53 | ### ADDED 54 | 55 | - There is now a `PoolBox` which works like `Box` but uses the pool to allocate. 56 | 57 | ## [0.3.0] - 2020-03-10 58 | 59 | ### REMOVED 60 | 61 | - The `PoolSync` mode has been removed entirely, along with the option to choose which mode to 62 | use, as `PoolUnsync` is now the only one remaining. `PoolSync` pools were too slow to be 63 | worthwhile, and I didn't trust the correctness of the threadsafe `PoolRef` implementation. 64 | 65 | ## [0.2.3] - 2020-01-07 66 | 67 | ### ADDED 68 | 69 | - `Pool` now implements `Debug`. 70 | 71 | ### FIXED 72 | 73 | - `Option` and `Pool` are now once again the same size. Zero sized pools still don't cause 74 | any allocations. 75 | 76 | ## [0.2.2] - 2019-12-16 77 | 78 | ### ADDED 79 | 80 | - You can now `Pool::cast()` a pool handle into a pool handle for a different type, allowing you 81 | to construct values of multiple types from the same pool, provided they are of the exact same 82 | size and alignment. 83 | - `Pool`s of size 0 are now represented by null pointers, meaning they allocate nothing. It also 84 | means `Option` is no longer identical in size to `Pool`, but `PoolRef` still retains that 85 | property. A `Pool` of size 0 is also conceptually identical to the `None` value of an 86 | `Option`, except you can use it to construct values without having to unwrap it first, so 87 | there's no good reason you should ever need `Option`. 88 | 89 | ## [0.2.1] - 2019-12-12 90 | 91 | ### FIXED 92 | 93 | - `Pool` and `PoolRef` now use `NonNull` instead of raw pointers, so that they can be wrapped in 94 | `Option` without growing in size. 95 | - Fixed a race condition where the last `PoolRef` referencing a pool might try to drop it before 96 | returning its allocation to it, causing a memory fault. 97 | 98 | ## [0.2.0] - 2019-11-29 99 | 100 | ### CHANGED 101 | 102 | - The pool is now reference counted, which means your `PoolRef`s won't suddenly become dangerously 103 | invalid when the pool goes out of scope. This also means that you can now clone a `Pool` and get 104 | another reference to the same pool. 105 | 106 | ### ADDED 107 | 108 | - There are now both `Sync` and `!Sync` versions of the pool. The latter, in 109 | `refpool::unsync::Pool`, is as performant as previously, while the thread safe version in 110 | `refpool::sync::Pool` is roughly 5-10x slower, but still manages to be about 25% faster than the 111 | Windows system allocator. You should prefer not to use it on platforms with faster system 112 | allocators, such as Linux. To enable the thread safe version, use the `sync` feature flag. 113 | - A method `Pool::fill()` has been added, which preallocates memory chunks to the capacity of the 114 | pool. 115 | 116 | ## [0.1.0] - 2019-11-26 117 | 118 | Initial release. 119 | -------------------------------------------------------------------------------- /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, gender identity and expression, level of experience, 9 | education, socio-economic status, nationality, personal appearance, race, 10 | 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 contacting the project team at admin@immutable.rs. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement 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 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "refpool" 3 | version = "0.4.3" 4 | authors = ["Bodil Stokke "] 5 | edition = "2018" 6 | license = "MPL-2.0+" 7 | description = "Efficient memory pool with reference counting" 8 | repository = "https://github.com/bodil/refpool" 9 | documentation = "http://docs.rs/refpool" 10 | readme = "./README.md" 11 | categories = ["memory-management"] 12 | keywords = ["rc", "memory-pool"] 13 | exclude = ["release.toml", "proptest-regressions/**"] 14 | 15 | [features] 16 | default = [] 17 | default_impl = [] 18 | 19 | [[bench]] 20 | name = "pool" 21 | harness = false 22 | 23 | [dev-dependencies] 24 | criterion = "0.3.0" 25 | -------------------------------------------------------------------------------- /LICENCE.md: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | ### 1. Definitions 5 | 6 | **1.1. “Contributor”** 7 | means each individual or legal entity that creates, contributes to 8 | the creation of, or owns Covered Software. 9 | 10 | **1.2. “Contributor Version”** 11 | means the combination of the Contributions of others (if any) used 12 | by a Contributor and that particular Contributor's Contribution. 13 | 14 | **1.3. “Contribution”** 15 | means Covered Software of a particular Contributor. 16 | 17 | **1.4. “Covered Software”** 18 | means Source Code Form to which the initial Contributor has attached 19 | the notice in Exhibit A, the Executable Form of such Source Code 20 | Form, and Modifications of such Source Code Form, in each case 21 | including portions thereof. 22 | 23 | **1.5. “Incompatible With Secondary Licenses”** 24 | means 25 | 26 | * **(a)** that the initial Contributor has attached the notice described 27 | in Exhibit B to the Covered Software; or 28 | * **(b)** that the Covered Software was made available under the terms of 29 | version 1.1 or earlier of the License, but not also under the 30 | terms of a Secondary License. 31 | 32 | **1.6. “Executable Form”** 33 | means any form of the work other than Source Code Form. 34 | 35 | **1.7. “Larger Work”** 36 | means a work that combines Covered Software with other material, in 37 | a separate file or files, that is not Covered Software. 38 | 39 | **1.8. “License”** 40 | means this document. 41 | 42 | **1.9. “Licensable”** 43 | means having the right to grant, to the maximum extent possible, 44 | whether at the time of the initial grant or subsequently, any and 45 | all of the rights conveyed by this License. 46 | 47 | **1.10. “Modifications”** 48 | means any of the following: 49 | 50 | * **(a)** any file in Source Code Form that results from an addition to, 51 | deletion from, or modification of the contents of Covered 52 | Software; or 53 | * **(b)** any new file in Source Code Form that contains any Covered 54 | Software. 55 | 56 | **1.11. “Patent Claims” of a Contributor** 57 | means any patent claim(s), including without limitation, method, 58 | process, and apparatus claims, in any patent Licensable by such 59 | Contributor that would be infringed, but for the grant of the 60 | License, by the making, using, selling, offering for sale, having 61 | made, import, or transfer of either its Contributions or its 62 | Contributor Version. 63 | 64 | **1.12. “Secondary License”** 65 | means either the GNU General Public License, Version 2.0, the GNU 66 | Lesser General Public License, Version 2.1, the GNU Affero General 67 | Public License, Version 3.0, or any later versions of those 68 | licenses. 69 | 70 | **1.13. “Source Code Form”** 71 | means the form of the work preferred for making modifications. 72 | 73 | **1.14. “You” (or “Your”)** 74 | means an individual or a legal entity exercising rights under this 75 | License. For legal entities, “You” includes any entity that 76 | controls, is controlled by, or is under common control with You. For 77 | purposes of this definition, “control” means **(a)** the power, direct 78 | or indirect, to cause the direction or management of such entity, 79 | whether by contract or otherwise, or **(b)** ownership of more than 80 | fifty percent (50%) of the outstanding shares or beneficial 81 | ownership of such entity. 82 | 83 | 84 | ### 2. License Grants and Conditions 85 | 86 | #### 2.1. Grants 87 | 88 | Each Contributor hereby grants You a world-wide, royalty-free, 89 | non-exclusive license: 90 | 91 | * **(a)** under intellectual property rights (other than patent or trademark) 92 | Licensable by such Contributor to use, reproduce, make available, 93 | modify, display, perform, distribute, and otherwise exploit its 94 | Contributions, either on an unmodified basis, with Modifications, or 95 | as part of a Larger Work; and 96 | * **(b)** under Patent Claims of such Contributor to make, use, sell, offer 97 | for sale, have made, import, and otherwise transfer either its 98 | Contributions or its Contributor Version. 99 | 100 | #### 2.2. Effective Date 101 | 102 | The licenses granted in Section 2.1 with respect to any Contribution 103 | become effective for each Contribution on the date the Contributor first 104 | distributes such Contribution. 105 | 106 | #### 2.3. Limitations on Grant Scope 107 | 108 | The licenses granted in this Section 2 are the only rights granted under 109 | this License. No additional rights or licenses will be implied from the 110 | distribution or licensing of Covered Software under this License. 111 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 112 | Contributor: 113 | 114 | * **(a)** for any code that a Contributor has removed from Covered Software; 115 | or 116 | * **(b)** for infringements caused by: **(i)** Your and any other third party's 117 | modifications of Covered Software, or **(ii)** the combination of its 118 | Contributions with other software (except as part of its Contributor 119 | Version); or 120 | * **(c)** under Patent Claims infringed by Covered Software in the absence of 121 | its Contributions. 122 | 123 | This License does not grant any rights in the trademarks, service marks, 124 | or logos of any Contributor (except as may be necessary to comply with 125 | the notice requirements in Section 3.4). 126 | 127 | #### 2.4. Subsequent Licenses 128 | 129 | No Contributor makes additional grants as a result of Your choice to 130 | distribute the Covered Software under a subsequent version of this 131 | License (see Section 10.2) or under the terms of a Secondary License (if 132 | permitted under the terms of Section 3.3). 133 | 134 | #### 2.5. Representation 135 | 136 | Each Contributor represents that the Contributor believes its 137 | Contributions are its original creation(s) or it has sufficient rights 138 | to grant the rights to its Contributions conveyed by this License. 139 | 140 | #### 2.6. Fair Use 141 | 142 | This License is not intended to limit any rights You have under 143 | applicable copyright doctrines of fair use, fair dealing, or other 144 | equivalents. 145 | 146 | #### 2.7. Conditions 147 | 148 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 149 | in Section 2.1. 150 | 151 | 152 | ### 3. Responsibilities 153 | 154 | #### 3.1. Distribution of Source Form 155 | 156 | All distribution of Covered Software in Source Code Form, including any 157 | Modifications that You create or to which You contribute, must be under 158 | the terms of this License. You must inform recipients that the Source 159 | Code Form of the Covered Software is governed by the terms of this 160 | License, and how they can obtain a copy of this License. You may not 161 | attempt to alter or restrict the recipients' rights in the Source Code 162 | Form. 163 | 164 | #### 3.2. Distribution of Executable Form 165 | 166 | If You distribute Covered Software in Executable Form then: 167 | 168 | * **(a)** such Covered Software must also be made available in Source Code 169 | Form, as described in Section 3.1, and You must inform recipients of 170 | the Executable Form how they can obtain a copy of such Source Code 171 | Form by reasonable means in a timely manner, at a charge no more 172 | than the cost of distribution to the recipient; and 173 | 174 | * **(b)** You may distribute such Executable Form under the terms of this 175 | License, or sublicense it under different terms, provided that the 176 | license for the Executable Form does not attempt to limit or alter 177 | the recipients' rights in the Source Code Form under this License. 178 | 179 | #### 3.3. Distribution of a Larger Work 180 | 181 | You may create and distribute a Larger Work under terms of Your choice, 182 | provided that You also comply with the requirements of this License for 183 | the Covered Software. If the Larger Work is a combination of Covered 184 | Software with a work governed by one or more Secondary Licenses, and the 185 | Covered Software is not Incompatible With Secondary Licenses, this 186 | License permits You to additionally distribute such Covered Software 187 | under the terms of such Secondary License(s), so that the recipient of 188 | the Larger Work may, at their option, further distribute the Covered 189 | Software under the terms of either this License or such Secondary 190 | License(s). 191 | 192 | #### 3.4. Notices 193 | 194 | You may not remove or alter the substance of any license notices 195 | (including copyright notices, patent notices, disclaimers of warranty, 196 | or limitations of liability) contained within the Source Code Form of 197 | the Covered Software, except that You may alter any license notices to 198 | the extent required to remedy known factual inaccuracies. 199 | 200 | #### 3.5. Application of Additional Terms 201 | 202 | You may choose to offer, and to charge a fee for, warranty, support, 203 | indemnity or liability obligations to one or more recipients of Covered 204 | Software. However, You may do so only on Your own behalf, and not on 205 | behalf of any Contributor. You must make it absolutely clear that any 206 | such warranty, support, indemnity, or liability obligation is offered by 207 | You alone, and You hereby agree to indemnify every Contributor for any 208 | liability incurred by such Contributor as a result of warranty, support, 209 | indemnity or liability terms You offer. You may include additional 210 | disclaimers of warranty and limitations of liability specific to any 211 | jurisdiction. 212 | 213 | 214 | ### 4. Inability to Comply Due to Statute or Regulation 215 | 216 | If it is impossible for You to comply with any of the terms of this 217 | License with respect to some or all of the Covered Software due to 218 | statute, judicial order, or regulation then You must: **(a)** comply with 219 | the terms of this License to the maximum extent possible; and **(b)** 220 | describe the limitations and the code they affect. Such description must 221 | be placed in a text file included with all distributions of the Covered 222 | Software under this License. Except to the extent prohibited by statute 223 | or regulation, such description must be sufficiently detailed for a 224 | recipient of ordinary skill to be able to understand it. 225 | 226 | 227 | ### 5. Termination 228 | 229 | **5.1.** The rights granted under this License will terminate automatically 230 | if You fail to comply with any of its terms. However, if You become 231 | compliant, then the rights granted under this License from a particular 232 | Contributor are reinstated **(a)** provisionally, unless and until such 233 | Contributor explicitly and finally terminates Your grants, and **(b)** on an 234 | ongoing basis, if such Contributor fails to notify You of the 235 | non-compliance by some reasonable means prior to 60 days after You have 236 | come back into compliance. Moreover, Your grants from a particular 237 | Contributor are reinstated on an ongoing basis if such Contributor 238 | notifies You of the non-compliance by some reasonable means, this is the 239 | first time You have received notice of non-compliance with this License 240 | from such Contributor, and You become compliant prior to 30 days after 241 | Your receipt of the notice. 242 | 243 | **5.2.** If You initiate litigation against any entity by asserting a patent 244 | infringement claim (excluding declaratory judgment actions, 245 | counter-claims, and cross-claims) alleging that a Contributor Version 246 | directly or indirectly infringes any patent, then the rights granted to 247 | You by any and all Contributors for the Covered Software under Section 248 | 2.1 of this License shall terminate. 249 | 250 | **5.3.** In the event of termination under Sections 5.1 or 5.2 above, all 251 | end user license agreements (excluding distributors and resellers) which 252 | have been validly granted by You or Your distributors under this License 253 | prior to termination shall survive termination. 254 | 255 | 256 | ### 6. Disclaimer of Warranty 257 | 258 | > Covered Software is provided under this License on an “as is” 259 | > basis, without warranty of any kind, either expressed, implied, or 260 | > statutory, including, without limitation, warranties that the 261 | > Covered Software is free of defects, merchantable, fit for a 262 | > particular purpose or non-infringing. The entire risk as to the 263 | > quality and performance of the Covered Software is with You. 264 | > Should any Covered Software prove defective in any respect, You 265 | > (not any Contributor) assume the cost of any necessary servicing, 266 | > repair, or correction. This disclaimer of warranty constitutes an 267 | > essential part of this License. No use of any Covered Software is 268 | > authorized under this License except under this disclaimer. 269 | 270 | ### 7. Limitation of Liability 271 | 272 | > Under no circumstances and under no legal theory, whether tort 273 | > (including negligence), contract, or otherwise, shall any 274 | > Contributor, or anyone who distributes Covered Software as 275 | > permitted above, be liable to You for any direct, indirect, 276 | > special, incidental, or consequential damages of any character 277 | > including, without limitation, damages for lost profits, loss of 278 | > goodwill, work stoppage, computer failure or malfunction, or any 279 | > and all other commercial damages or losses, even if such party 280 | > shall have been informed of the possibility of such damages. This 281 | > limitation of liability shall not apply to liability for death or 282 | > personal injury resulting from such party's negligence to the 283 | > extent applicable law prohibits such limitation. Some 284 | > jurisdictions do not allow the exclusion or limitation of 285 | > incidental or consequential damages, so this exclusion and 286 | > limitation may not apply to You. 287 | 288 | 289 | ### 8. Litigation 290 | 291 | Any litigation relating to this License may be brought only in the 292 | courts of a jurisdiction where the defendant maintains its principal 293 | place of business and such litigation shall be governed by laws of that 294 | jurisdiction, without reference to its conflict-of-law provisions. 295 | Nothing in this Section shall prevent a party's ability to bring 296 | cross-claims or counter-claims. 297 | 298 | 299 | ### 9. Miscellaneous 300 | 301 | This License represents the complete agreement concerning the subject 302 | matter hereof. If any provision of this License is held to be 303 | unenforceable, such provision shall be reformed only to the extent 304 | necessary to make it enforceable. Any law or regulation which provides 305 | that the language of a contract shall be construed against the drafter 306 | shall not be used to construe this License against a Contributor. 307 | 308 | 309 | ### 10. Versions of the License 310 | 311 | #### 10.1. New Versions 312 | 313 | Mozilla Foundation is the license steward. Except as provided in Section 314 | 10.3, no one other than the license steward has the right to modify or 315 | publish new versions of this License. Each version will be given a 316 | distinguishing version number. 317 | 318 | #### 10.2. Effect of New Versions 319 | 320 | You may distribute the Covered Software under the terms of the version 321 | of the License under which You originally received the Covered Software, 322 | or under the terms of any subsequent version published by the license 323 | steward. 324 | 325 | #### 10.3. Modified Versions 326 | 327 | If you create software not governed by this License, and you want to 328 | create a new license for such software, you may create and use a 329 | modified version of this License if you rename the license and remove 330 | any references to the name of the license steward (except to note that 331 | such modified license differs from this License). 332 | 333 | #### 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses 334 | 335 | If You choose to distribute Source Code Form that is Incompatible With 336 | Secondary Licenses under the terms of this version of the License, the 337 | notice described in Exhibit B of this License must be attached. 338 | 339 | ## Exhibit A - Source Code Form License Notice 340 | 341 | This Source Code Form is subject to the terms of the Mozilla Public 342 | License, v. 2.0. If a copy of the MPL was not distributed with this 343 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 344 | 345 | If it is not possible or desirable to put the notice in a particular 346 | file, then You may include the notice in a location (such as a LICENSE 347 | file in a relevant directory) where a recipient would be likely to look 348 | for such a notice. 349 | 350 | You may add additional accurate notices of copyright ownership. 351 | 352 | ## Exhibit B - “Incompatible With Secondary Licenses” Notice 353 | 354 | This Source Code Form is "Incompatible With Secondary Licenses", as 355 | defined by the Mozilla Public License, v. 2.0. 356 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # refpool 2 | 3 | A reimplementation of Rust's `std::boxed::Box` and `std::rc::Rc` which uses a pool of reusable memory 4 | to speed up reallocation. 5 | 6 | ## Is It Fast? 7 | 8 | It's about twice as fast as the system allocator on Linux systems, and six times 9 | as fast on Windows systems, when the pool is non-empty. For certain data types, 10 | gains can be even higher. 11 | 12 | ## Documentation 13 | 14 | * [API docs](https://docs.rs/refpool) 15 | 16 | ## Licence 17 | 18 | Copyright 2019 Bodil Stokke 19 | 20 | This software is subject to the terms of the Mozilla Public 21 | License, v. 2.0. If a copy of the MPL was not distributed with this 22 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 23 | 24 | ## Code of Conduct 25 | 26 | Please note that this project is released with a [Contributor Code of 27 | Conduct][coc]. By participating in this project you agree to abide by its 28 | terms. 29 | 30 | [coc]: https://github.com/bodil/refpool/blob/master/CODE_OF_CONDUCT.md 31 | -------------------------------------------------------------------------------- /benches/pool.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::rc::Rc; 6 | 7 | use criterion::{ 8 | black_box, criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion, Throughput, 9 | }; 10 | 11 | use refpool::{Pool, PoolDefault, PoolRef}; 12 | 13 | const SIZES: &[usize] = &[1024, 2048, 4096, 8192, 16384, 32768, 65536, 131_072]; 14 | 15 | struct BigLumpOfUsize([usize; 1024]); 16 | 17 | impl Default for BigLumpOfUsize { 18 | fn default() -> Self { 19 | Self([0; 1024]) 20 | } 21 | } 22 | 23 | pub fn alloc(name: &str, c: &mut Criterion) { 24 | let mut group = c.benchmark_group(name); 25 | for size in SIZES { 26 | group.throughput(Throughput::Elements(*size as u64)); 27 | group.bench_with_input(BenchmarkId::new("sysalloc", size), size, |b, &size| { 28 | b.iter_batched_ref( 29 | || Vec::with_capacity(size), 30 | |vec| { 31 | for _ in 0..size { 32 | vec.push(P::default()); 33 | } 34 | }, 35 | BatchSize::SmallInput, 36 | ) 37 | }); 38 | group.bench_with_input(BenchmarkId::new("empty pool", size), size, |b, &size| { 39 | b.iter_batched_ref( 40 | || (Pool::::new(size), Vec::with_capacity(size)), 41 | |&mut (ref pool, ref mut vec)| { 42 | for _ in 0..size { 43 | vec.push(PoolRef::default(pool)); 44 | } 45 | }, 46 | BatchSize::SmallInput, 47 | ); 48 | }); 49 | group.bench_with_input(BenchmarkId::new("full pool", size), size, |b, &size| { 50 | b.iter_batched_ref( 51 | || { 52 | let pool = Pool::::new(size); 53 | pool.fill(); 54 | (pool, Vec::with_capacity(size)) 55 | }, 56 | |&mut (ref pool, ref mut vec)| { 57 | for _ in 0..size { 58 | vec.push(PoolRef::default(pool)); 59 | } 60 | }, 61 | BatchSize::SmallInput, 62 | ); 63 | }); 64 | } 65 | group.finish(); 66 | } 67 | 68 | pub fn realloc(name: &str, c: &mut Criterion) { 69 | let mut group = c.benchmark_group(name); 70 | for size in SIZES { 71 | group.throughput(Throughput::Elements(*size as u64)); 72 | group.bench_with_input(BenchmarkId::new("sysalloc", size), size, |b, &size| { 73 | b.iter(|| { 74 | for _ in 0..size { 75 | black_box(P::default()); 76 | } 77 | }) 78 | }); 79 | group.bench_with_input(BenchmarkId::new("pool", size), size, |b, &size| { 80 | b.iter_batched_ref( 81 | || Pool::::new(size), 82 | |pool| { 83 | for _ in 0..size { 84 | black_box(PoolRef::default(pool)); 85 | } 86 | }, 87 | BatchSize::SmallInput, 88 | ); 89 | }); 90 | } 91 | group.finish(); 92 | } 93 | 94 | fn alloc_usize(c: &mut Criterion) { 95 | alloc::>("alloc/usize", c) 96 | } 97 | 98 | fn realloc_usize(c: &mut Criterion) { 99 | realloc::>("realloc/size", c) 100 | } 101 | 102 | fn alloc_1024x_usize(c: &mut Criterion) { 103 | alloc::>("alloc/1024xusize", c) 104 | } 105 | 106 | fn realloc_1024x_usize(c: &mut Criterion) { 107 | realloc::>("realloc/1024xsize", c) 108 | } 109 | 110 | criterion_group!( 111 | refpool, 112 | alloc_usize, 113 | realloc_usize, 114 | alloc_1024x_usize, 115 | realloc_1024x_usize 116 | ); 117 | criterion_main!(refpool); 118 | -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | target 3 | corpus 4 | artifacts 5 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "refpool-fuzz" 3 | version = "0.0.0" 4 | authors = ["Automatically generated"] 5 | publish = false 6 | edition = "2018" 7 | 8 | [package.metadata] 9 | cargo-fuzz = true 10 | 11 | [dependencies] 12 | libfuzzer-sys = "0.3" 13 | arbitrary = { version = "0.4.4", features = ["derive"] } 14 | 15 | [dependencies.refpool] 16 | path = ".." 17 | 18 | # Prevent this from interfering with workspaces 19 | [workspace] 20 | members = ["."] 21 | 22 | [[bin]] 23 | name = "refpool" 24 | path = "fuzz_targets/refpool.rs" 25 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/refpool.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use arbitrary::Arbitrary; 3 | use libfuzzer_sys::fuzz_target; 4 | use refpool::{Pool, PoolRef}; 5 | 6 | #[derive(Arbitrary, Debug)] 7 | enum Action { 8 | Allocate(String), 9 | AllocateDefault, 10 | Deallocate(usize), 11 | CloneNew(usize), 12 | Unwrap(usize), 13 | } 14 | 15 | use self::Action::*; 16 | 17 | fn alloc(allocs: &[A], index: usize) -> Option { 18 | if allocs.is_empty() { 19 | None 20 | } else { 21 | Some(index % allocs.len()) 22 | } 23 | } 24 | 25 | fuzz_target!(|input: (u16, Vec)| { 26 | let pool = Pool::new(input.0 as usize); 27 | let actions = input.1; 28 | let mut allocs = Vec::new(); 29 | for action in actions { 30 | match action { 31 | Allocate(data) => { 32 | allocs.push(PoolRef::new(&pool, data)); 33 | } 34 | AllocateDefault => { 35 | allocs.push(PoolRef::::default(&pool)); 36 | } 37 | Deallocate(index) => { 38 | if let Some(index) = alloc(&allocs, index) { 39 | allocs.remove(index); 40 | } 41 | } 42 | CloneNew(index) => { 43 | if let Some(index) = alloc(&allocs, index) { 44 | allocs.push(PoolRef::cloned(&pool, &allocs[index])); 45 | } 46 | } 47 | Unwrap(index) => { 48 | if let Some(index) = alloc(&allocs, index) { 49 | let string = PoolRef::unwrap_or_clone(allocs.remove(index)); 50 | allocs.push(PoolRef::new(&pool, string)); 51 | } 52 | } 53 | } 54 | } 55 | }); 56 | -------------------------------------------------------------------------------- /release.toml: -------------------------------------------------------------------------------- 1 | no-dev-version = true 2 | sign-commit = true 3 | pre-release-replacements = [ { file="CHANGELOG.md", search="\\[Unreleased\\]", replace="[{{version}}] - {{date}}" } ] 4 | -------------------------------------------------------------------------------- /src/box_handle.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use crate::pointer::Pointer; 6 | use crate::pool::Pool; 7 | use crate::refbox::assume_init; 8 | use crate::refbox::data_ptr; 9 | use crate::refbox::RefBox; 10 | use crate::PoolClone; 11 | use crate::{types::ElementPointer, PoolDefault}; 12 | use std::borrow::Borrow; 13 | use std::borrow::BorrowMut; 14 | use std::cmp::Ordering; 15 | use std::fmt::Debug; 16 | use std::fmt::Display; 17 | use std::fmt::Error; 18 | use std::fmt::Formatter; 19 | use std::hash::Hash; 20 | use std::hash::Hasher; 21 | use std::ops::DerefMut; 22 | use std::ptr::NonNull; 23 | use std::{ops::Deref, pin::Pin}; 24 | 25 | /// A unique pointer to a pool allocated value of `A`. 26 | pub struct PoolBox { 27 | pub(crate) handle: ElementPointer, 28 | } 29 | 30 | impl PoolBox { 31 | /// Construct a `PoolBox` with a newly initialised value of `A`. 32 | /// 33 | /// This uses [`PoolDefault::default_uninit()`][default_uninit] to initialise a 34 | /// default value, which may be faster than constructing a `PoolBox` from an 35 | /// existing value using [`PoolBox::new()`][new], depending on the data 36 | /// type. 37 | /// 38 | /// # Examples 39 | /// 40 | /// ```rust 41 | /// # use refpool::{Pool, PoolBox}; 42 | /// let pool: Pool = Pool::new(256); 43 | /// let zero = PoolBox::default(&pool); 44 | /// assert_eq!(0, *zero); 45 | /// ``` 46 | /// 47 | /// [new]: #method.new 48 | /// [default_uninit]: trait.PoolDefault.html#tymethod.default_uninit 49 | pub fn default(pool: &Pool) -> Self 50 | where 51 | A: PoolDefault, 52 | { 53 | let mut handle = pool.pop(); 54 | unsafe { 55 | PoolDefault::default_uninit(data_ptr(&mut handle)); 56 | assume_init(handle) 57 | } 58 | .into_box() 59 | } 60 | 61 | /// Wrap a value in a `PoolBox`. 62 | /// 63 | /// This will copy the entire value into the memory handled by the 64 | /// `PoolBox`, which may be slower than using 65 | /// [`PoolBox::default()`][default], so it's not recommended to use this to 66 | /// construct the default value. 67 | /// 68 | /// # Examples 69 | /// 70 | /// ```rust 71 | /// # use refpool::{Pool, PoolBox}; 72 | /// let pool: Pool = Pool::new(256); 73 | /// let number = PoolBox::new(&pool, 1337); 74 | /// assert_eq!(1337, *number); 75 | /// ``` 76 | /// 77 | /// [default]: #method.default 78 | pub fn new(pool: &Pool, value: A) -> Self { 79 | let mut handle = pool.pop(); 80 | unsafe { 81 | data_ptr(&mut handle).as_mut_ptr().write(value); 82 | assume_init(handle) 83 | } 84 | .into_box() 85 | } 86 | 87 | /// Clone a value and return a new `PoolBox` to it. 88 | /// 89 | /// This will use [`PoolClone::clone_uninit()`][clone_uninit] to perform the 90 | /// clone, which may be more efficient than using 91 | /// [`PoolBox::new(value.clone())`][new]. 92 | /// 93 | /// # Examples 94 | /// 95 | /// ```rust 96 | /// # use refpool::{Pool, PoolBox}; 97 | /// let pool: Pool> = Pool::new(1); 98 | /// let vec = vec![1, 2, 3]; 99 | /// let ref1 = PoolBox::clone_from(&pool, &vec); 100 | /// assert_eq!(vec, *ref1); 101 | /// ``` 102 | /// 103 | /// [new]: #method.new 104 | /// [clone_uninit]: trait.PoolClone.html#tymethod.clone_uninit 105 | pub fn clone_from(pool: &Pool, value: &A) -> Self 106 | where 107 | A: PoolClone, 108 | { 109 | let mut handle = pool.pop(); 110 | unsafe { 111 | value.clone_uninit(data_ptr(&mut handle)); 112 | assume_init(handle) 113 | } 114 | .into_box() 115 | } 116 | 117 | /// Construct a [`Pin`][Pin]ned `PoolBox` with a default value. 118 | /// 119 | /// # Examples 120 | /// 121 | /// ```rust 122 | /// # use refpool::{Pool, PoolBox}; 123 | /// let pool: Pool = Pool::new(256); 124 | /// let zero = PoolBox::pin_default(&pool); 125 | /// assert_eq!(0, *zero); 126 | /// ``` 127 | /// 128 | /// [Pin]: https://doc.rust-lang.org/std/pin/struct.Pin.html 129 | pub fn pin_default(pool: &Pool) -> Pin 130 | where 131 | A: PoolDefault, 132 | { 133 | unsafe { Pin::new_unchecked(Self::default(pool)) } 134 | } 135 | 136 | /// Construct a [`Pin`][Pin]ned `PoolBox` with the given value. 137 | /// 138 | /// # Examples 139 | /// 140 | /// ```rust 141 | /// # use refpool::{Pool, PoolBox}; 142 | /// let pool: Pool = Pool::new(256); 143 | /// let number = PoolBox::pin(&pool, 1337); 144 | /// assert_eq!(1337, *number); 145 | /// ``` 146 | /// 147 | /// [Pin]: https://doc.rust-lang.org/std/pin/struct.Pin.html 148 | pub fn pin(pool: &Pool, value: A) -> Pin { 149 | unsafe { Pin::new_unchecked(Self::new(pool, value)) } 150 | } 151 | 152 | /// Test two `PoolBox`es for pointer equality. 153 | /// 154 | /// # Examples 155 | /// 156 | /// ```rust 157 | /// # use refpool::{Pool, PoolBox}; 158 | /// let pool: Pool = Pool::new(1); 159 | /// let ref1 = PoolBox::default(&pool); 160 | /// assert!(PoolBox::ptr_eq(&ref1, &ref1)); 161 | /// ``` 162 | pub fn ptr_eq(left: &Self, right: &Self) -> bool { 163 | std::ptr::eq(left.handle.get_ptr(), right.handle.get_ptr()) 164 | } 165 | 166 | /// Consume the `PoolBox` and return a pointer to the contents. 167 | /// 168 | /// Please note that the only proper way to drop the value pointed to 169 | /// is by using `PoolBox::from_raw` to turn it back into a `PoolBox`, because 170 | /// the value is followed by `PoolBox` metadata which also needs to 171 | /// be dropped. 172 | pub fn into_raw_non_null(b: PoolBox) -> NonNull { 173 | let ptr = b.handle.cast(); 174 | std::mem::forget(b); 175 | ptr 176 | } 177 | 178 | /// Consume the `PoolBox` and return a pointer to the contents. 179 | /// 180 | /// The pointer is guaranteed to be non-null. 181 | /// 182 | /// Please note that the only proper way to drop the value pointed to 183 | /// is by using `PoolBox::from_raw` to turn it back into a `PoolBox`, because 184 | /// the value is followed by `PoolBox` metadata which also needs to 185 | /// be dropped. 186 | pub fn into_raw(b: PoolBox) -> *mut A { 187 | Self::into_raw_non_null(b).as_ptr() 188 | } 189 | 190 | /// Turn a raw pointer back into a `PoolBox`. 191 | /// 192 | /// The pointer must be non-null and obtained from a previous call to 193 | /// `PoolBox::into_raw` or `PoolBox::into_raw_non_null`. 194 | /// 195 | /// # Safety 196 | /// 197 | /// This must *only* be called on pointers obtained through `PoolBox::into_raw`. 198 | /// It's not OK to call it on a pointer to a value of `A` you've allocated 199 | /// yourself. 200 | /// 201 | /// # Examples 202 | /// 203 | /// ```rust 204 | /// # use refpool::{Pool, PoolBox}; 205 | /// let pool: Pool = Pool::new(1); 206 | /// let ref1 = PoolBox::new(&pool, 31337); 207 | /// 208 | /// // Turn the PoolBox into a raw pointer and see if it still works. 209 | /// let ptr = PoolBox::into_raw(ref1); 210 | /// assert_eq!(31337, unsafe { *ptr }); 211 | /// 212 | /// // Turn it back into a PoolBox and see, again, if it still works. 213 | /// let ref2 = unsafe { PoolBox::from_raw(ptr) }; 214 | /// assert_eq!(31337, *ref2); 215 | /// ``` 216 | pub unsafe fn from_raw(ptr: *mut A) -> Self { 217 | Self { 218 | handle: ElementPointer::wrap(ptr.cast()), 219 | } 220 | } 221 | 222 | fn box_ref(&self) -> &RefBox { 223 | unsafe { &*self.handle.get_ptr() } 224 | } 225 | 226 | fn box_ref_mut(&mut self) -> &mut RefBox { 227 | unsafe { &mut *self.handle.get_ptr() } 228 | } 229 | } 230 | 231 | impl Drop for PoolBox { 232 | fn drop(&mut self) { 233 | let handle = unsafe { Box::from_raw(self.handle.get_ptr()) }; 234 | handle.return_to_pool(); 235 | } 236 | } 237 | 238 | impl Clone for PoolBox 239 | where 240 | A: PoolClone, 241 | { 242 | /// Clone a `PoolBox` and its contents. 243 | /// 244 | /// This will use [`PoolClone::clone_uninit()`][clone_uninit] to perform the 245 | /// clone, which may be more efficient than using 246 | /// [`PoolBox::new(value.clone())`][new]. 247 | /// 248 | /// # Examples 249 | /// 250 | /// ```rust 251 | /// # use refpool::{Pool, PoolBox}; 252 | /// let pool: Pool> = Pool::new(1); 253 | /// let vec1 = PoolBox::new(&pool, vec![1, 2, 3]); 254 | /// let vec2 = vec1.clone(); 255 | /// assert_eq!(vec1, vec2); 256 | /// ``` 257 | /// 258 | /// [new]: #method.new 259 | /// [clone_uninit]: trait.PoolClone.html#tymethod.clone_uninit 260 | fn clone(&self) -> Self { 261 | let mut handle = self.box_ref().pool.pop(); 262 | unsafe { 263 | self.deref().clone_uninit(data_ptr(&mut handle)); 264 | assume_init(handle) 265 | } 266 | .into_box() 267 | } 268 | } 269 | 270 | impl Deref for PoolBox { 271 | type Target = A; 272 | fn deref(&self) -> &Self::Target { 273 | self.box_ref().value_as_ref() 274 | } 275 | } 276 | 277 | impl DerefMut for PoolBox { 278 | fn deref_mut(&mut self) -> &mut Self::Target { 279 | self.box_ref_mut().value_as_mut() 280 | } 281 | } 282 | 283 | impl AsRef for PoolBox { 284 | fn as_ref(&self) -> &A { 285 | self.deref() 286 | } 287 | } 288 | 289 | impl AsMut for PoolBox { 290 | fn as_mut(&mut self) -> &mut A { 291 | self.deref_mut() 292 | } 293 | } 294 | 295 | impl Borrow for PoolBox { 296 | fn borrow(&self) -> &A { 297 | self.deref() 298 | } 299 | } 300 | 301 | impl BorrowMut for PoolBox { 302 | fn borrow_mut(&mut self) -> &mut A { 303 | self.deref_mut() 304 | } 305 | } 306 | 307 | impl PartialEq for PoolBox 308 | where 309 | A: PartialEq, 310 | { 311 | fn eq(&self, other: &Self) -> bool { 312 | (**self) == (**other) 313 | } 314 | } 315 | 316 | impl Eq for PoolBox where A: Eq {} 317 | 318 | impl PartialOrd for PoolBox 319 | where 320 | A: PartialOrd, 321 | { 322 | fn partial_cmp(&self, other: &Self) -> Option { 323 | (**self).partial_cmp(&**other) 324 | } 325 | } 326 | 327 | impl Ord for PoolBox 328 | where 329 | A: Ord, 330 | { 331 | fn cmp(&self, other: &Self) -> Ordering { 332 | (**self).cmp(&**other) 333 | } 334 | } 335 | 336 | impl Hash for PoolBox 337 | where 338 | A: Hash, 339 | { 340 | fn hash(&self, hasher: &mut H) { 341 | (**self).hash(hasher) 342 | } 343 | } 344 | 345 | impl Display for PoolBox 346 | where 347 | A: Display, 348 | { 349 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 350 | (**self).fmt(f) 351 | } 352 | } 353 | 354 | impl Debug for PoolBox 355 | where 356 | A: Debug, 357 | { 358 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 359 | (**self).fmt(f) 360 | } 361 | } 362 | 363 | impl std::fmt::Pointer for PoolBox { 364 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 365 | std::fmt::Pointer::fmt(&(&**self as *const A), f) 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /src/counter.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | pub(crate) trait Counter: Default { 6 | fn inc(&mut self); 7 | fn dec(&mut self) -> usize; 8 | fn count(&self) -> usize; 9 | } 10 | 11 | impl Counter for usize { 12 | #[inline(always)] 13 | fn inc(&mut self) { 14 | *self += 1; 15 | } 16 | 17 | #[inline(always)] 18 | fn dec(&mut self) -> usize { 19 | let prev = *self; 20 | *self -= 1; 21 | prev 22 | } 23 | 24 | #[inline(always)] 25 | fn count(&self) -> usize { 26 | *self 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/fakepool.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | //! Fake versions of `Pool`, `PoolRef` and `PoolBox`. 6 | //! 7 | //! This module provides zero cost wrappers for `Box` and `Rc`, as well as 8 | //! a zero sized empty `Pool`, which are code compatible with their 9 | //! real counterparts. These can be useful if you're writing code that only 10 | //! optionally uses pooled allocation, allowing you to use the same code for 11 | //! both situations, differing only in which versions of `Pool` and friends 12 | //! you choose to import. 13 | 14 | #![allow(dead_code, missing_docs, clippy::missing_safety_doc)] 15 | 16 | use std::marker::PhantomData; 17 | use std::{ 18 | borrow::{Borrow, BorrowMut}, 19 | cmp::Ordering, 20 | fmt::{Debug, Display, Error, Formatter}, 21 | hash::{Hash, Hasher}, 22 | ops::{Deref, DerefMut}, 23 | pin::Pin, 24 | rc::Rc, 25 | }; 26 | 27 | use crate::{PoolClone, PoolDefault}; 28 | 29 | /// A fake `Pool` which is always empty. 30 | /// 31 | /// Note that, unlike its non-fake counterpart, this pool will always report its 32 | /// current and maximum sizes as zero, regardless of the value passed into the 33 | /// constructor, and it will always report itself as being full, to be consistent 34 | /// with the reported sizes. You should therefore avoid assuming that the size 35 | /// passed into `Pool::new(size)` will have any bearing on the actual size of the 36 | /// pool if you're writing code that might be using a fake pool. 37 | pub struct Pool(PhantomData); 38 | 39 | impl Pool { 40 | pub fn new(_size: usize) -> Self { 41 | Pool(PhantomData) 42 | } 43 | 44 | pub fn get_max_size(&self) -> usize { 45 | 0 46 | } 47 | 48 | pub fn get_pool_size(&self) -> usize { 49 | 0 50 | } 51 | 52 | pub fn is_full(&self) -> bool { 53 | true 54 | } 55 | 56 | pub fn fill(&self) {} 57 | 58 | pub fn cast(&self) -> Pool { 59 | Pool(PhantomData) 60 | } 61 | } 62 | 63 | impl Clone for Pool { 64 | fn clone(&self) -> Self { 65 | Self::new(0) 66 | } 67 | } 68 | 69 | impl Debug for Pool { 70 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { 71 | write!(f, "FakePool") 72 | } 73 | } 74 | 75 | /// A fake `PoolRef` which wraps an `Rc`. 76 | #[derive(Default)] 77 | pub struct PoolRef(Rc); 78 | 79 | impl PoolRef { 80 | #[inline(always)] 81 | pub fn default(_pool: &Pool) -> Self 82 | where 83 | A: PoolDefault, 84 | { 85 | Self(Default::default()) 86 | } 87 | 88 | #[inline(always)] 89 | pub fn new(_pool: &Pool, value: A) -> Self { 90 | Self(Rc::new(value)) 91 | } 92 | 93 | #[inline(always)] 94 | pub fn clone_from(_pool: &Pool, value: &A) -> Self 95 | where 96 | A: PoolClone, 97 | { 98 | Self(Rc::new(value.clone())) 99 | } 100 | 101 | #[inline(always)] 102 | pub fn pin_default(_pool: &Pool) -> Pin 103 | where 104 | A: PoolDefault, 105 | { 106 | unsafe { Pin::new_unchecked(Self(Rc::new(A::default()))) } 107 | } 108 | 109 | #[inline(always)] 110 | pub fn pin(_pool: &Pool, value: A) -> Pin { 111 | unsafe { Pin::new_unchecked(Self(Rc::new(value))) } 112 | } 113 | 114 | #[inline(always)] 115 | pub fn cloned(_pool: &Pool, this: &Self) -> Self 116 | where 117 | A: PoolClone, 118 | { 119 | Self(Rc::new(this.deref().clone())) 120 | } 121 | 122 | #[inline(always)] 123 | pub fn make_mut<'a>(_pool: &Pool, this: &'a mut Self) -> &'a mut A 124 | where 125 | A: PoolClone, 126 | { 127 | Rc::make_mut(&mut this.0) 128 | } 129 | 130 | #[inline(always)] 131 | pub fn get_mut(this: &mut Self) -> Option<&mut A> { 132 | Rc::get_mut(&mut this.0) 133 | } 134 | 135 | #[inline(always)] 136 | pub fn try_unwrap(this: Self) -> Result { 137 | Rc::try_unwrap(this.0).map_err(Self) 138 | } 139 | 140 | #[inline(always)] 141 | pub fn unwrap_or_clone(this: Self) -> A 142 | where 143 | A: PoolClone, 144 | { 145 | Self::try_unwrap(this).unwrap_or_else(|this| this.deref().clone()) 146 | } 147 | 148 | #[inline(always)] 149 | pub fn ptr_eq(left: &Self, right: &Self) -> bool { 150 | Rc::ptr_eq(&left.0, &right.0) 151 | } 152 | 153 | #[inline(always)] 154 | pub fn strong_count(this: &Self) -> usize { 155 | Rc::strong_count(&this.0) 156 | } 157 | 158 | #[inline(always)] 159 | pub fn into_raw(this: PoolRef) -> *const A { 160 | Rc::into_raw(this.0) 161 | } 162 | 163 | #[inline(always)] 164 | pub unsafe fn from_raw(ptr: *mut A) -> Self { 165 | Self(Rc::from_raw(ptr)) 166 | } 167 | } 168 | 169 | impl Clone for PoolRef { 170 | #[inline(always)] 171 | fn clone(&self) -> Self { 172 | PoolRef(self.0.clone()) 173 | } 174 | } 175 | 176 | impl Deref for PoolRef { 177 | type Target = A; 178 | #[inline(always)] 179 | fn deref(&self) -> &Self::Target { 180 | self.0.deref() 181 | } 182 | } 183 | 184 | impl AsRef for PoolRef { 185 | fn as_ref(&self) -> &A { 186 | self.deref() 187 | } 188 | } 189 | 190 | impl Borrow for PoolRef { 191 | fn borrow(&self) -> &A { 192 | self.deref() 193 | } 194 | } 195 | 196 | impl PartialEq for PoolRef 197 | where 198 | A: PartialEq, 199 | { 200 | #[inline(always)] 201 | fn eq(&self, other: &Self) -> bool { 202 | **self == **other 203 | } 204 | } 205 | 206 | impl Eq for PoolRef where A: Eq {} 207 | 208 | impl PartialOrd for PoolRef 209 | where 210 | A: PartialOrd, 211 | { 212 | fn partial_cmp(&self, other: &Self) -> Option { 213 | (**self).partial_cmp(&**other) 214 | } 215 | } 216 | 217 | impl Ord for PoolRef 218 | where 219 | A: Ord, 220 | { 221 | fn cmp(&self, other: &Self) -> Ordering { 222 | (**self).cmp(&**other) 223 | } 224 | } 225 | 226 | impl Hash for PoolRef 227 | where 228 | A: Hash, 229 | { 230 | fn hash(&self, hasher: &mut H) { 231 | (**self).hash(hasher) 232 | } 233 | } 234 | 235 | impl Display for PoolRef 236 | where 237 | A: Display, 238 | { 239 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 240 | (**self).fmt(f) 241 | } 242 | } 243 | 244 | impl Debug for PoolRef 245 | where 246 | A: Debug, 247 | { 248 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 249 | (**self).fmt(f) 250 | } 251 | } 252 | 253 | impl std::fmt::Pointer for PoolRef { 254 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 255 | std::fmt::Pointer::fmt(&(&**self as *const A), f) 256 | } 257 | } 258 | 259 | /// A fake `PoolBox` which wraps a `Box`. 260 | pub struct PoolBox(Box); 261 | 262 | impl PoolBox { 263 | #[inline(always)] 264 | pub fn default(_pool: &Pool) -> Self 265 | where 266 | A: PoolDefault, 267 | { 268 | Self(Box::new(A::default())) 269 | } 270 | 271 | #[inline(always)] 272 | pub fn new(_pool: &Pool, value: A) -> Self { 273 | Self(Box::new(value)) 274 | } 275 | 276 | #[inline(always)] 277 | pub fn clone_from(_pool: &Pool, value: &A) -> Self 278 | where 279 | A: PoolClone, 280 | { 281 | Self(Box::new(value.clone())) 282 | } 283 | 284 | #[inline(always)] 285 | pub fn pin_default(_pool: &Pool) -> Pin 286 | where 287 | A: PoolDefault, 288 | { 289 | unsafe { Pin::new_unchecked(Self(Box::new(A::default()))) } 290 | } 291 | 292 | #[inline(always)] 293 | pub fn pin(_pool: &Pool, value: A) -> Pin { 294 | unsafe { Pin::new_unchecked(Self(Box::new(value))) } 295 | } 296 | 297 | #[inline(always)] 298 | pub fn ptr_eq(left: &Self, right: &Self) -> bool { 299 | std::ptr::eq(left.0.deref(), right.0.deref()) 300 | } 301 | 302 | #[inline(always)] 303 | pub fn into_raw(this: Self) -> *mut A { 304 | Box::into_raw(this.0) 305 | } 306 | 307 | #[inline(always)] 308 | pub unsafe fn from_raw(ptr: *mut A) -> Self { 309 | Self(Box::from_raw(ptr)) 310 | } 311 | } 312 | 313 | impl Deref for PoolBox { 314 | type Target = A; 315 | #[inline(always)] 316 | fn deref(&self) -> &Self::Target { 317 | self.0.deref() 318 | } 319 | } 320 | 321 | impl DerefMut for PoolBox { 322 | fn deref_mut(&mut self) -> &mut Self::Target { 323 | self.0.deref_mut() 324 | } 325 | } 326 | 327 | impl AsRef for PoolBox { 328 | fn as_ref(&self) -> &A { 329 | self.deref() 330 | } 331 | } 332 | 333 | impl AsMut for PoolBox { 334 | fn as_mut(&mut self) -> &mut A { 335 | self.deref_mut() 336 | } 337 | } 338 | 339 | impl Borrow for PoolBox { 340 | fn borrow(&self) -> &A { 341 | self.deref() 342 | } 343 | } 344 | 345 | impl BorrowMut for PoolBox { 346 | fn borrow_mut(&mut self) -> &mut A { 347 | self.deref_mut() 348 | } 349 | } 350 | 351 | impl PartialEq for PoolBox 352 | where 353 | A: PartialEq, 354 | { 355 | fn eq(&self, other: &Self) -> bool { 356 | (**self) == (**other) 357 | } 358 | } 359 | 360 | impl Eq for PoolBox where A: Eq {} 361 | 362 | impl PartialOrd for PoolBox 363 | where 364 | A: PartialOrd, 365 | { 366 | fn partial_cmp(&self, other: &Self) -> Option { 367 | (**self).partial_cmp(&**other) 368 | } 369 | } 370 | 371 | impl Ord for PoolBox 372 | where 373 | A: Ord, 374 | { 375 | fn cmp(&self, other: &Self) -> Ordering { 376 | (**self).cmp(&**other) 377 | } 378 | } 379 | 380 | impl Hash for PoolBox 381 | where 382 | A: Hash, 383 | { 384 | fn hash(&self, hasher: &mut H) { 385 | (**self).hash(hasher) 386 | } 387 | } 388 | 389 | impl Display for PoolBox 390 | where 391 | A: Display, 392 | { 393 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 394 | (**self).fmt(f) 395 | } 396 | } 397 | 398 | impl Debug for PoolBox 399 | where 400 | A: Debug, 401 | { 402 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 403 | (**self).fmt(f) 404 | } 405 | } 406 | 407 | impl std::fmt::Pointer for PoolBox { 408 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 409 | std::fmt::Pointer::fmt(&(&**self as *const A), f) 410 | } 411 | } 412 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | //! A reimplementation of [`std::boxed::Box`][Box] and [`std::rc::Rc`][Rc] 6 | //! which uses a pool of reusable memory to speed up reallocation. 7 | //! 8 | //! # Prerequisites 9 | //! 10 | //! In order to initialise a type to its default value from the memory pool 11 | //! using [`PoolBox::default()`][PoolBox::default] or 12 | //! [`PoolRef::default()`][PoolRef::default], it needs to implement 13 | //! [`PoolDefault`][PoolDefault]. 14 | //! 15 | //! If you want to be able to use [`PoolRef::make_mut()`][PoolRef::make_mut], it 16 | //! also needs to implement [`PoolClone`][PoolClone]. 17 | //! 18 | //! For constructing values using [`PoolRef::new()`][PoolRef::new], there's no 19 | //! requirement. 20 | //! 21 | //! There are implementations for [`PoolDefault`][PoolDefault] and 22 | //! [`PoolClone`][PoolClone] for most primitive types and a good selection of 23 | //! `std`'s data types, and you can easily provide default implementations for 24 | //! your own types by implementing the marker trait 25 | //! [`PoolDefaultImpl`][PoolDefaultImpl]. You can also implement your own if you 26 | //! have data structures whose memory doesn't need to be fully intitialised at 27 | //! construction time, which can give you a slight performance boost. (This 28 | //! optimisation is why [`PoolDefault`][PoolDefault] and 29 | //! [`PoolClone`][PoolClone] exist as distinct traits, otherwise 30 | //! [`Default`][Default] and [`Clone`][Clone] would have sufficed.) 31 | //! 32 | //! # Usage 33 | //! 34 | //! You create new values by calling 35 | //! [`PoolRef::default(pool)`][PoolRef::default] or [`PoolRef::new(pool, 36 | //! value)`][PoolRef::new]. This will use memory from the pool if available, 37 | //! falling back to a normal heap allocation if the pool is empty. When the 38 | //! last [`PoolRef`][PoolRef] referencing the value is dropped, its allocated 39 | //! memory is returned to the pool. 40 | //! 41 | //! # Differences from [`Box`][Box] and [`Rc`][Rc] 42 | //! 43 | //! [`PoolBox`][PoolBox] is API compatible with [`Box`][Box] and [`PoolRef`][PoolRef] 44 | //! with [`Rc`][Rc], with the following exceptions: 45 | //! 46 | //! * Types handled by the pool must be [`Sized`][Sized]. This means the pool 47 | //! won't accept trait objects, ie. no `Pool`. 48 | //! * Constructors need a [`Pool`][Pool] argument, so they're necessarily 49 | //! different: instead of [`Rc::new(value)`][Rc::new], you have 50 | //! [`PoolRef::default(pool)`][PoolRef::default] to construct a default 51 | //! value and [`PoolRef::new(pool, value)`][PoolRef::new] as the equivalent 52 | //! of [`Rc::new(value)`][Rc::new]. Likewise for [`PoolBox`][PoolBox]. 53 | //! * [`PoolBox`][PoolBox] and [`PoolRef`][PoolRef] do not implement 54 | //! [`Default`][Default], because you need a 55 | //! [`Pool`][Pool] argument to construct an instance. Use 56 | //! [`PoolRef::default(pool)`][PoolRef::default]. 57 | //! * There's currently no equivalent to [`Weak`][Weak] for [`PoolRef`][PoolRef]. 58 | //! * Experimental APIs are not implemented. 59 | //! 60 | //! # Thread Safety 61 | //! 62 | //! [`Pool`][Pool] is strictly thread local, ie. it does not 63 | //! implement [`Sync`][Sync] and it will fail in appalling ways if you still 64 | //! somehow manage to access it from two different threads. There is no 65 | //! equivalent of [`Arc`][Arc] because adding thread safety to the pool turns 66 | //! out to degrade performance sufficiently that the pool is no longer providing 67 | //! a significant performance benefit even with the slowest system allocators 68 | //! you're likely to come across in the wild (by which I mean Windows). 69 | //! 70 | //! # Performance 71 | //! 72 | //! You can expect [`Pool`][Pool] to always outperform the system allocator, 73 | //! though the performance gains will vary between platforms. Preliminary 74 | //! benchmarks show it's approximately twice as fast on Linux, and 5-6 times as 75 | //! fast on Windows. Custom allocators like jemalloc may yield even less 76 | //! benefit, but it's very unlikely you'll find an allocator that can outperform 77 | //! the pool. 78 | //! 79 | //! You can expect bigger performance gains from data types with beneficial 80 | //! [`PoolDefault`][PoolDefault] and [`PoolClone`][PoolClone] implementations, 81 | //! "beneficial" in this case meaning cases where you can leave most of the 82 | //! allocated memory uninitialised. [`sized_chunks::Chunk`][Chunk], which 83 | //! allocates 528 bytes on 64-bit platforms but only needs to initialise 16 84 | //! of them for [`PoolDefault`][PoolDefault], would be a good example of this. 85 | 86 | //! # Example 87 | //! 88 | //! ```rust 89 | //! # use refpool::{Pool, PoolRef}; 90 | //! // Create a pool of `usize` with a max size of 1 (for argument's sake). 91 | //! let mut pool: Pool = Pool::new(1); 92 | //! 93 | //! { 94 | //! // Create a reference handle to a usize initialised to 0. 95 | //! // The pool starts out empty, so this triggers a normal heap alloc. 96 | //! let value_ref = PoolRef::default(&mut pool); 97 | //! assert_eq!(0, *value_ref); // You can deref it just like `Rc`. 98 | //! } // `value_ref` is dropped here, and its heap memory goes on the pool. 99 | //! 100 | //! // Check that we do indeed have one allocation in the pool now. 101 | //! assert_eq!(1, pool.get_pool_size()); 102 | //! 103 | //! // Create another reference and initialise it to 31337, a good round number. 104 | //! // This will reuse `value_ref`'s memory. 105 | //! let another_value_ref = PoolRef::new(&mut pool, 31337); 106 | //! assert_eq!(31337, *another_value_ref); 107 | //! 108 | //! // Check that the pool is again empty after we reused the previous memory. 109 | //! assert_eq!(0, pool.get_pool_size()); 110 | //! ``` 111 | //! 112 | //! # Feature Flags 113 | //! 114 | //! There's one feature flag available, `default_impl`, which requires a nightly 115 | //! rustc because it leans on the `min_specialization` language feature, which 116 | //! removes the `PoolDefaultImpl` trait and instead provides a `default` 117 | //! overridable implementation for `PoolClone` and `PoolDefault` for any type 118 | //! that implements `Clone` and `Default`. `PoolDefaultImpl` is an unfortunate 119 | //! hack to get around the current absence of specialisation in stable rustc. 120 | //! 121 | //! [Pool]: struct.Pool.html 122 | //! [PoolBox]: struct.PoolBox.html 123 | //! [PoolBox::default]: struct.PoolBox.html#method.default 124 | //! [PoolRef]: struct.PoolRef.html 125 | //! [PoolRef::new]: struct.PoolRef.html#method.new 126 | //! [PoolRef::default]: struct.PoolRef.html#method.default 127 | //! [PoolRef::make_mut]: struct.PoolRef.html#method.make_mut 128 | //! [PoolDefault]: trait.PoolDefault.html 129 | //! [PoolClone]: trait.PoolClone.html 130 | //! [PoolDefaultImpl]: trait.PoolDefaultImpl.html 131 | //! [PoolSync]: struct.PoolSync.html 132 | //! [Box]: https://doc.rust-lang.org/stable/std/boxed/struct.Box.html 133 | //! [Box::from_raw]: https://doc.rust-lang.org/stable/std/boxed/struct.Box.html#method.from_raw 134 | //! [Box::into_raw]: https://doc.rust-lang.org/stable/std/boxed/struct.Box.html#method.into_raw 135 | //! [Default]: https://doc.rust-lang.org/std/default/trait.Default.html 136 | //! [Clone]: https://doc.rust-lang.org/std/clone/trait.Clone.html 137 | //! [Arc]: https://doc.rust-lang.org/std/sync/struct.Arc.html 138 | //! [Rc]: https://doc.rust-lang.org/std/rc/struct.Rc.html 139 | //! [Rc::new]: https://doc.rust-lang.org/std/rc/struct.Rc.html#method.new 140 | //! [Weak]: https://doc.rust-lang.org/std/rc/struct.Weak.html 141 | //! [Sized]: https://doc.rust-lang.org/std/marker/trait.Sized.html 142 | //! [Sync]: https://doc.rust-lang.org/std/marker/trait.Sync.html 143 | //! [Chunk]: https://docs.rs/sized-chunks/*/sized_chunks/sized_chunk/struct.Chunk.html 144 | 145 | #![forbid(rust_2018_idioms)] 146 | #![deny(nonstandard_style)] 147 | #![warn( 148 | unreachable_pub, 149 | missing_docs, 150 | missing_debug_implementations, 151 | missing_doc_code_examples 152 | )] 153 | #![cfg_attr(feature = "default_impl", feature(min_specialization))] 154 | 155 | use std::mem::MaybeUninit; 156 | 157 | mod box_handle; 158 | mod counter; 159 | mod pointer; 160 | mod pool; 161 | mod ref_handle; 162 | mod refbox; 163 | mod stack; 164 | mod types; 165 | 166 | pub mod fakepool; 167 | 168 | pub use self::box_handle::PoolBox; 169 | pub use self::pool::Pool; 170 | pub use self::ref_handle::PoolRef; 171 | 172 | #[cfg(not(feature = "default_impl"))] 173 | mod std_types; 174 | #[cfg(not(feature = "default_impl"))] 175 | pub use self::std_types::PoolDefaultImpl; 176 | 177 | /// A trait for initialising a `MaybeUninit` to a default value. 178 | pub trait PoolDefault: Default { 179 | /// Initialise an instance of `Self` to its default state. 180 | /// 181 | /// Specifically, after calling `self.default_uninit()`, the object's state 182 | /// should be equal to what `::default()` would produce. 183 | /// 184 | /// # Safety 185 | /// 186 | /// You should assume that the object as passed to you contains 187 | /// uninitialised memory, and you must leave it in a fully initialised 188 | /// state, as expected by `MaybeUninit::assume_init()`. 189 | unsafe fn default_uninit(target: &mut MaybeUninit); 190 | } 191 | 192 | /// A trait for cloning a value into a `MaybeUninit`. 193 | pub trait PoolClone: Clone { 194 | /// Clone an instance of `Self` into an uninitialised instance of `Self`. 195 | /// 196 | /// # Safety 197 | /// 198 | /// You should assume that the object as passed to you contains 199 | /// uninitialised memory, and you must leave it in a fully initialised 200 | /// state, as expected by `MaybeUninit::assume_init()`. 201 | unsafe fn clone_uninit(&self, target: &mut MaybeUninit); 202 | } 203 | 204 | #[cfg(feature = "default_impl")] 205 | impl PoolDefault for A 206 | where 207 | A: Default, 208 | { 209 | default unsafe fn default_uninit(target: &mut MaybeUninit) { 210 | target.as_mut_ptr().write(Default::default()); 211 | } 212 | } 213 | 214 | #[cfg(feature = "default_impl")] 215 | impl PoolClone for A 216 | where 217 | A: Clone, 218 | { 219 | default unsafe fn clone_uninit(&self, target: &mut MaybeUninit) { 220 | target.as_mut_ptr().write(self.clone()); 221 | } 222 | } 223 | 224 | #[cfg(test)] 225 | mod test { 226 | use super::*; 227 | use std::sync::atomic::{AtomicUsize, Ordering}; 228 | 229 | struct DropTest<'a> { 230 | counter: &'a AtomicUsize, 231 | } 232 | 233 | impl<'a> DropTest<'a> { 234 | fn new(counter: &'a AtomicUsize) -> Self { 235 | counter.fetch_add(1, Ordering::Relaxed); 236 | DropTest { counter } 237 | } 238 | } 239 | 240 | impl<'a> Drop for DropTest<'a> { 241 | fn drop(&mut self) { 242 | self.counter.fetch_sub(1, Ordering::Relaxed); 243 | } 244 | } 245 | 246 | fn fill_drop(pool_size: usize, alloc_size: usize) { 247 | let counter = AtomicUsize::new(0); 248 | let pool: Pool> = Pool::new(pool_size); 249 | { 250 | let mut vec = Vec::new(); 251 | for _ in 0..alloc_size { 252 | vec.push(PoolRef::new(&pool, DropTest::new(&counter))); 253 | } 254 | assert_eq!(alloc_size, counter.load(Ordering::SeqCst)); 255 | } 256 | assert_eq!(0, counter.load(Ordering::SeqCst)); 257 | } 258 | 259 | #[test] 260 | fn dropping_sized() { 261 | fill_drop(1024, 2048); 262 | } 263 | 264 | #[test] 265 | fn dropping_null() { 266 | fill_drop(0, 128); 267 | } 268 | 269 | #[test] 270 | fn allocate_and_deallocate_a_bit() { 271 | let pool: Pool = Pool::new(1024); 272 | assert_eq!(0, pool.get_pool_size()); 273 | let mut refs: Vec<_> = Vec::new(); 274 | for _ in 0..10000 { 275 | refs.push(PoolRef::default(&pool)); 276 | } 277 | assert_eq!(0, pool.get_pool_size()); 278 | refs.clear(); 279 | assert_eq!(1024, pool.get_pool_size()); 280 | for _ in 0..10000 { 281 | refs.push(PoolRef::default(&pool)); 282 | } 283 | assert_eq!(0, pool.get_pool_size()); 284 | let mut refs2 = refs.clone(); 285 | assert_eq!(refs, refs2); 286 | for (left, right) in refs.iter().zip(refs2.iter()) { 287 | assert!(PoolRef::ptr_eq(left, right)); 288 | } 289 | refs.clear(); 290 | assert_eq!(0, pool.get_pool_size()); 291 | refs2.clear(); 292 | assert_eq!(1024, pool.get_pool_size()); 293 | } 294 | 295 | #[test] 296 | fn null_pool_antics() { 297 | let pool: Pool = Pool::new(0); 298 | assert_eq!(0, pool.get_pool_size()); 299 | let mut refs: Vec<_> = Vec::new(); 300 | for _ in 0..10000 { 301 | refs.push(PoolRef::default(&pool)); 302 | } 303 | assert_eq!(0, pool.get_pool_size()); 304 | refs.clear(); 305 | assert_eq!(0, pool.get_pool_size()); 306 | for _ in 0..10000 { 307 | refs.push(PoolRef::default(&pool)); 308 | } 309 | assert_eq!(0, pool.get_pool_size()); 310 | let mut refs2 = refs.clone(); 311 | assert_eq!(refs, refs2); 312 | for (left, right) in refs.iter().zip(refs2.iter()) { 313 | assert!(PoolRef::ptr_eq(left, right)); 314 | } 315 | refs.clear(); 316 | assert_eq!(0, pool.get_pool_size()); 317 | refs2.clear(); 318 | assert_eq!(0, pool.get_pool_size()); 319 | } 320 | 321 | #[test] 322 | fn unwrap_or_clone() { 323 | let pool: Pool = Pool::new(1024); 324 | let val = PoolRef::new(&pool, 1337); 325 | // This would crash if unwrap_or_clone tries to drop the consumed PoolRef. 326 | let unwrapped = PoolRef::unwrap_or_clone(val); 327 | assert_eq!(1337, unwrapped); 328 | } 329 | 330 | #[test] 331 | fn option_of_ref_size_equals_ref_size() { 332 | use std::mem::size_of; 333 | assert_eq!( 334 | size_of::>(), 335 | size_of::>>() 336 | ); 337 | assert_eq!(size_of::>(), size_of::>>()); 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /src/pointer.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::ptr::NonNull; 6 | 7 | pub(crate) trait Pointer: std::fmt::Pointer { 8 | fn wrap(ptr: *mut A) -> Self; 9 | fn get_ptr(&self) -> *mut A; 10 | fn cast(self) -> *mut B; 11 | fn get_ptr_checked(&self) -> Option<*mut A>; 12 | fn null() -> Self; 13 | } 14 | 15 | impl Pointer for NonNull { 16 | #[inline(always)] 17 | fn wrap(ptr: *mut A) -> Self { 18 | debug_assert_eq!(false, ptr.is_null()); 19 | unsafe { NonNull::new_unchecked(ptr) } 20 | } 21 | 22 | #[inline(always)] 23 | fn get_ptr(&self) -> *mut A { 24 | self.as_ptr() 25 | } 26 | 27 | #[inline(always)] 28 | fn cast(self) -> *mut B { 29 | self.as_ptr().cast() 30 | } 31 | 32 | #[inline(always)] 33 | fn get_ptr_checked(&self) -> Option<*mut A> { 34 | if *self == NonNull::dangling() { 35 | None 36 | } else { 37 | Some(self.as_ptr()) 38 | } 39 | } 40 | 41 | #[inline(always)] 42 | fn null() -> Self { 43 | NonNull::dangling() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/pool.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::fmt::{Debug, Error, Formatter}; 6 | use std::mem::MaybeUninit; 7 | 8 | use crate::counter::Counter; 9 | use crate::pointer::Pointer; 10 | use crate::refbox::RefBox; 11 | use crate::stack::Stack; 12 | use crate::types::{ElementPointer, PoolPointer}; 13 | 14 | unsafe fn init_box(ref_box: *mut RefBox, pool: Pool) { 15 | let count_ptr: *mut _ = &mut (*(ref_box)).count; 16 | let pool_ptr: *mut _ = &mut (*(ref_box)).pool; 17 | count_ptr.write(Default::default()); 18 | pool_ptr.write(pool); 19 | } 20 | 21 | /// A pool of preallocated memory sized to match type `A`. 22 | /// 23 | /// In order to use it to allocate objects, pass it to 24 | /// [`PoolRef::new()`][PoolRef::new] or [`PoolRef::default()`][PoolRef::default]. 25 | /// 26 | /// # Example 27 | /// 28 | /// ```rust 29 | /// # use refpool::{Pool, PoolRef}; 30 | /// let mut pool: Pool = Pool::new(1024); 31 | /// let pool_ref = PoolRef::new(&mut pool, 31337); 32 | /// assert_eq!(31337, *pool_ref); 33 | /// ``` 34 | /// 35 | /// [PoolRef::new]: struct.PoolRef.html#method.new 36 | /// [PoolRef::default]: struct.PoolRef.html#method.default 37 | 38 | pub struct Pool { 39 | inner: PoolPointer, 40 | } 41 | 42 | impl Pool { 43 | /// Construct a new pool with a given max size and return a handle to it. 44 | /// 45 | /// Values constructed via the pool will be returned to the pool when 46 | /// dropped, up to `max_size`. When the pool is full, values will be dropped 47 | /// in the regular way. 48 | /// 49 | /// If `max_size` is `0`, meaning the pool can never hold any dropped 50 | /// values, this method will give you back a null handle without allocating 51 | /// a pool. You can still use this to construct `PoolRef` values, they'll 52 | /// just allocate in the old fashioned way without using a pool. It is 53 | /// therefore advisable to use a zero size pool as a null value instead of 54 | /// `Option`, which eliminates the need for unwrapping the `Option` 55 | /// value. 56 | pub fn new(max_size: usize) -> Self { 57 | if max_size == 0 { 58 | Self { 59 | inner: PoolPointer::null(), 60 | } 61 | } else { 62 | Box::new(PoolInner::new(max_size)).into_ref() 63 | } 64 | } 65 | 66 | pub(crate) fn push(&self, value: ElementPointer) { 67 | debug_assert!(self.inner.get_ptr_checked().is_some()); 68 | unsafe { (*self.inner.get_ptr()).push(value) } 69 | } 70 | 71 | pub(crate) fn pop(&self) -> Box>> { 72 | let mut obj = if let Some(inner) = self.inner.get_ptr_checked() { 73 | unsafe { (*inner).pop() } 74 | } else { 75 | None 76 | } 77 | .unwrap_or_else(|| Box::new(MaybeUninit::uninit())); 78 | unsafe { init_box(obj.as_mut_ptr(), self.clone()) }; 79 | obj 80 | } 81 | 82 | fn deref(&self) -> Option<&PoolInner> { 83 | self.inner.get_ptr_checked().map(|p| unsafe { &*p }) 84 | } 85 | 86 | /// Get the maximum size of the pool. 87 | pub fn get_max_size(&self) -> usize { 88 | self.deref().map(|p| p.get_max_size()).unwrap_or(0) 89 | } 90 | 91 | /// Get the current size of the pool. 92 | pub fn get_pool_size(&self) -> usize { 93 | self.deref().map(|p| p.get_pool_size()).unwrap_or(0) 94 | } 95 | 96 | /// Test if the pool is currently full. 97 | pub fn is_full(&self) -> bool { 98 | self.deref() 99 | .map(|p| p.get_pool_size() >= p.get_max_size()) 100 | .unwrap_or(true) 101 | } 102 | 103 | /// Fill the pool with empty allocations. 104 | /// 105 | /// This operation will pre-allocate `self.get_max_size() - 106 | /// self.get_pool_size()` memory chunks, without initialisation, and put 107 | /// them in the pool. 108 | /// 109 | /// # Examples 110 | /// 111 | /// ```rust 112 | /// # use refpool::{Pool, PoolRef}; 113 | /// let pool: Pool = Pool::new(1024); 114 | /// assert_eq!(0, pool.get_pool_size()); 115 | /// pool.fill(); 116 | /// assert_eq!(1024, pool.get_pool_size()); 117 | /// ``` 118 | pub fn fill(&self) { 119 | if let Some(inner) = self.deref() { 120 | while inner.get_max_size() > inner.get_pool_size() { 121 | let chunk = unsafe { 122 | std::alloc::alloc(std::alloc::Layout::from_size_align_unchecked( 123 | std::mem::size_of::>(), 124 | std::mem::align_of::>(), 125 | )) 126 | }; 127 | self.push(ElementPointer::wrap(chunk.cast())); 128 | } 129 | } 130 | } 131 | 132 | /// Fill the pool and return it. 133 | /// 134 | /// This is a convenience function that calls [`fill()`][fill] on 135 | /// the pool, so that you can construct a pool with a one liner: 136 | /// 137 | /// ```rust 138 | /// # use refpool::Pool; 139 | /// let pool: Pool = Pool::new(1024).filled(); 140 | /// assert!(pool.is_full()); 141 | /// ``` 142 | /// 143 | /// This is functionally equivalent to, but terser than: 144 | /// 145 | /// ```rust 146 | /// # use refpool::Pool; 147 | /// let mut pool: Pool = Pool::new(1024); 148 | /// pool.fill(); 149 | /// assert!(pool.is_full()); 150 | /// ``` 151 | /// 152 | /// [fill]: #method.fill 153 | pub fn filled(self) -> Self { 154 | self.fill(); 155 | self 156 | } 157 | 158 | /// Convert a pool handle for type `A` into a handle for type `B`. 159 | /// 160 | /// The types `A` and `B` must have the same size and alignment, as 161 | /// per [`std::mem::size_of`][size_of] and 162 | /// [`std::mem::align_of`][align_of], or this method will panic. 163 | /// 164 | /// This lets you use the same pool to construct values of different 165 | /// types, as long as they are of the same size and alignment, so 166 | /// they can reuse each others' memory allocations. 167 | /// 168 | /// # Examples 169 | /// 170 | /// ```rust 171 | /// # use refpool::{Pool, PoolRef}; 172 | /// # use std::convert::TryInto; 173 | /// let u64_pool: Pool = Pool::new(1024); 174 | /// let u64_number = PoolRef::new(&u64_pool, 1337); 175 | /// 176 | /// let i64_pool: Pool = u64_pool.cast(); 177 | /// let i64_number = PoolRef::new(&i64_pool, -1337); 178 | /// # assert_eq!(i64_number.abs().try_into(), Ok(*u64_number)); 179 | /// ``` 180 | /// 181 | /// [size_of]: https://doc.rust-lang.org/std/mem/fn.size_of.html 182 | /// [align_of]: https://doc.rust-lang.org/std/mem/fn.align_of.html 183 | pub fn cast(&self) -> Pool { 184 | assert!(std::mem::size_of::() == std::mem::size_of::()); 185 | assert!(std::mem::align_of::() >= std::mem::align_of::()); 186 | 187 | if let Some(ptr) = self.inner.get_ptr_checked() { 188 | let inner: *mut PoolInner = ptr.cast(); 189 | unsafe { (*inner).make_ref() } 190 | } else { 191 | Pool::new(0) 192 | } 193 | } 194 | } 195 | 196 | impl Clone for Pool { 197 | fn clone(&self) -> Self { 198 | if let Some(inner) = self.inner.get_ptr_checked() { 199 | unsafe { (*inner).make_ref() } 200 | } else { 201 | Self::new(0) 202 | } 203 | } 204 | } 205 | 206 | impl Drop for Pool { 207 | fn drop(&mut self) { 208 | if let Some(ptr) = self.inner.get_ptr_checked() { 209 | if unsafe { (*ptr).dec() } == 1 { 210 | std::mem::drop(unsafe { Box::from_raw(ptr) }); 211 | } 212 | } 213 | } 214 | } 215 | 216 | impl Debug for Pool { 217 | /// Debug implementation for `Pool`. 218 | /// 219 | /// # Examples 220 | /// 221 | /// ```rust 222 | /// # use refpool::Pool; 223 | /// let mut pool: Pool = Pool::new(256); 224 | /// assert!(format!("{:?}", pool).starts_with("Pool[0/256]:0x")); 225 | /// pool.fill(); 226 | /// assert!(format!("{:?}", pool).starts_with("Pool[256/256]:0x")); 227 | /// ``` 228 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 229 | write!( 230 | f, 231 | "Pool[{}/{}]:{:p}", 232 | self.get_pool_size(), 233 | self.get_max_size(), 234 | self.inner 235 | ) 236 | } 237 | } 238 | 239 | pub(crate) struct PoolInner { 240 | count: usize, 241 | max_size: usize, 242 | stack: Vec>, 243 | } 244 | 245 | impl PoolInner { 246 | fn new(max_size: usize) -> Self { 247 | Self { 248 | count: Default::default(), 249 | max_size, 250 | stack: Stack::stack_new(max_size), 251 | } 252 | } 253 | 254 | fn into_ref(mut self: Box) -> Pool { 255 | self.inc(); 256 | Pool { 257 | inner: PoolPointer::wrap(Box::into_raw(self)), 258 | } 259 | } 260 | 261 | fn make_ref(&mut self) -> Pool { 262 | self.inc(); 263 | Pool { 264 | inner: PoolPointer::wrap(self), 265 | } 266 | } 267 | 268 | /// Get the maximum size of the pool. 269 | fn get_max_size(&self) -> usize { 270 | self.max_size 271 | } 272 | 273 | /// Get the current size of the pool. 274 | fn get_pool_size(&self) -> usize { 275 | self.stack.stack_len() 276 | } 277 | 278 | #[inline(always)] 279 | fn inc(&mut self) { 280 | self.count.inc(); 281 | } 282 | 283 | #[inline(always)] 284 | fn dec(&mut self) -> usize { 285 | self.count.dec() 286 | } 287 | 288 | fn pop(&mut self) -> Option>>> { 289 | self.stack.stack_pop().map(|value_ptr| { 290 | let box_ptr = value_ptr.cast::>>(); 291 | unsafe { Box::from_raw(box_ptr.as_ptr()) } 292 | }) 293 | } 294 | 295 | fn push(&mut self, handle: ElementPointer) { 296 | self.stack.stack_push(handle); 297 | } 298 | } 299 | 300 | impl Drop for PoolInner { 301 | fn drop(&mut self) { 302 | while let Some(chunk) = self.stack.stack_pop() { 303 | unsafe { 304 | std::alloc::dealloc( 305 | chunk.as_ptr().cast(), 306 | std::alloc::Layout::from_size_align_unchecked( 307 | std::mem::size_of::>(), 308 | std::mem::align_of::>(), 309 | ), 310 | ); 311 | } 312 | } 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /src/ref_handle.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::borrow::Borrow; 6 | use std::cmp::Ordering; 7 | use std::fmt::{Debug, Display, Error, Formatter}; 8 | use std::hash::{Hash, Hasher}; 9 | use std::ops::Deref; 10 | use std::pin::Pin; 11 | 12 | use crate::counter::Counter; 13 | use crate::pointer::Pointer; 14 | use crate::refbox::{assume_init, data_ptr, RefBox}; 15 | use crate::types::ElementPointer; 16 | use crate::{Pool, PoolClone, PoolDefault}; 17 | 18 | /// A reference counted pointer to a pool allocated value of `A`. 19 | pub struct PoolRef { 20 | pub(crate) handle: ElementPointer, 21 | } 22 | 23 | impl PoolRef { 24 | /// Construct a `PoolRef` with a newly initialised value of `A`. 25 | /// 26 | /// This uses [`PoolDefault::default_uninit()`][default_uninit] to initialise a 27 | /// default value, which may be faster than constructing a `PoolRef` from an 28 | /// existing value using [`PoolRef::new()`][new], depending on the data 29 | /// type. 30 | /// 31 | /// # Examples 32 | /// 33 | /// ```rust 34 | /// # use refpool::{Pool, PoolRef}; 35 | /// let pool: Pool = Pool::new(256); 36 | /// let zero = PoolRef::default(&pool); 37 | /// assert_eq!(0, *zero); 38 | /// ``` 39 | /// 40 | /// [new]: #method.new 41 | /// [default_uninit]: trait.PoolDefault.html#tymethod.default_uninit 42 | pub fn default(pool: &Pool) -> Self 43 | where 44 | A: PoolDefault, 45 | { 46 | let mut handle = pool.pop(); 47 | unsafe { 48 | PoolDefault::default_uninit(data_ptr(&mut handle)); 49 | assume_init(handle) 50 | } 51 | .into_ref() 52 | } 53 | 54 | /// Wrap a value in a `PoolRef`. 55 | /// 56 | /// This will copy the entire value into the memory handled by the 57 | /// `PoolRef`, which may be slower than using 58 | /// [`PoolRef::default()`][default], so it's not recommended to use this to 59 | /// construct the default value. 60 | /// 61 | /// # Examples 62 | /// 63 | /// ```rust 64 | /// # use refpool::{Pool, PoolRef}; 65 | /// let pool: Pool = Pool::new(256); 66 | /// let number = PoolRef::new(&pool, 1337); 67 | /// assert_eq!(1337, *number); 68 | /// ``` 69 | /// 70 | /// [default]: #method.default 71 | pub fn new(pool: &Pool, value: A) -> Self { 72 | let mut handle = pool.pop(); 73 | unsafe { 74 | data_ptr(&mut handle).as_mut_ptr().write(value); 75 | assume_init(handle) 76 | } 77 | .into_ref() 78 | } 79 | 80 | /// Clone a value and return a new `PoolRef` to it. 81 | /// 82 | /// This will use [`PoolClone::clone_uninit()`][clone_uninit] to perform the 83 | /// clone, which may be more efficient than using 84 | /// [`PoolRef::new(value.clone())`][new]. 85 | /// 86 | /// # Examples 87 | /// 88 | /// ```rust 89 | /// # use refpool::{Pool, PoolRef}; 90 | /// let pool: Pool> = Pool::new(1); 91 | /// let vec = vec![1, 2, 3]; 92 | /// let ref1 = PoolRef::clone_from(&pool, &vec); 93 | /// assert_eq!(vec, *ref1); 94 | /// ``` 95 | /// 96 | /// [new]: #method.new 97 | /// [clone_uninit]: trait.PoolClone.html#tymethod.clone_uninit 98 | pub fn clone_from(pool: &Pool, value: &A) -> Self 99 | where 100 | A: PoolClone, 101 | { 102 | let mut handle = pool.pop(); 103 | unsafe { 104 | value.clone_uninit(data_ptr(&mut handle)); 105 | assume_init(handle) 106 | } 107 | .into_ref() 108 | } 109 | 110 | /// Construct a [`Pin`][Pin]ned `PoolRef` with a default value. 111 | /// 112 | /// # Examples 113 | /// 114 | /// ```rust 115 | /// # use refpool::{Pool, PoolRef}; 116 | /// let pool: Pool = Pool::new(256); 117 | /// let zero = PoolRef::pin_default(&pool); 118 | /// assert_eq!(0, *zero); 119 | /// ``` 120 | /// 121 | /// [Pin]: https://doc.rust-lang.org/std/pin/struct.Pin.html 122 | pub fn pin_default(pool: &Pool) -> Pin 123 | where 124 | A: PoolDefault, 125 | { 126 | unsafe { Pin::new_unchecked(Self::default(pool)) } 127 | } 128 | 129 | /// Construct a [`Pin`][Pin]ned `PoolRef` with the given value. 130 | /// 131 | /// # Examples 132 | /// 133 | /// ```rust 134 | /// # use refpool::{Pool, PoolRef}; 135 | /// let pool: Pool = Pool::new(256); 136 | /// let number = PoolRef::pin(&pool, 1337); 137 | /// assert_eq!(1337, *number); 138 | /// ``` 139 | /// 140 | /// [Pin]: https://doc.rust-lang.org/std/pin/struct.Pin.html 141 | pub fn pin(pool: &Pool, value: A) -> Pin { 142 | unsafe { Pin::new_unchecked(Self::new(pool, value)) } 143 | } 144 | 145 | /// Clone the value inside a `PoolRef` and return a new `PoolRef` to it. 146 | /// 147 | /// This will use [`PoolClone::clone_uninit()`][clone_uninit] to perform 148 | /// the clone, which may be more efficient than using 149 | /// [`PoolRef::new((*this_ref).clone())`][new]. 150 | /// 151 | /// # Examples 152 | /// 153 | /// ```rust 154 | /// # use refpool::{Pool, PoolRef}; 155 | /// let pool: Pool = Pool::new(256); 156 | /// let mut number = PoolRef::new(&pool, 1337); 157 | /// let other_number = PoolRef::cloned(&pool, &number); 158 | /// *PoolRef::make_mut(&pool, &mut number) = 123; 159 | /// assert_eq!(123, *number); 160 | /// assert_eq!(1337, *other_number); 161 | /// ``` 162 | /// 163 | /// [new]: #method.new 164 | /// [clone_uninit]: trait.PoolClone.html#tymethod.clone_uninit 165 | pub fn cloned(pool: &Pool, this: &Self) -> Self 166 | where 167 | A: PoolClone, 168 | { 169 | let mut handle = pool.pop(); 170 | unsafe { 171 | this.deref().clone_uninit(data_ptr(&mut handle)); 172 | assume_init(handle) 173 | } 174 | .into_ref() 175 | } 176 | 177 | /// Get a mutable reference to the value inside a `PoolRef`, cloning it 178 | /// first if this `PoolRef` isn't a unique reference. 179 | /// 180 | /// # Examples 181 | /// 182 | /// ```rust 183 | /// # use refpool::{Pool, PoolRef}; 184 | /// let pool: Pool = Pool::new(1); 185 | /// let ref1 = PoolRef::new(&pool, 1); 186 | /// let mut ref2 = ref1.clone(); 187 | /// *PoolRef::make_mut(&pool, &mut ref2) = 2; 188 | /// assert_eq!(1, *ref1); 189 | /// assert_eq!(2, *ref2); 190 | /// ``` 191 | pub fn make_mut<'a>(pool: &Pool, this: &'a mut Self) -> &'a mut A 192 | where 193 | A: PoolClone, 194 | { 195 | if this.box_ref().is_shared() { 196 | let mut new_handle = pool.pop(); 197 | let mut new_handle = unsafe { 198 | this.deref().deref().clone_uninit(data_ptr(&mut new_handle)); 199 | assume_init(new_handle) 200 | }; 201 | new_handle.inc(); 202 | this.box_ref_mut().dec(); 203 | this.handle = ElementPointer::wrap(Box::into_raw(new_handle)); 204 | } 205 | this.box_ref_mut().value_as_mut() 206 | } 207 | 208 | /// Attempt to get a mutable reference to the value inside a `PoolRef`. 209 | /// 210 | /// This will produce a `None` if this `PoolRef` isn't a unique reference 211 | /// to the value. 212 | /// 213 | /// # Examples 214 | /// 215 | /// ```rust 216 | /// # use refpool::{Pool, PoolRef}; 217 | /// let pool: Pool = Pool::new(128); 218 | /// let mut number = PoolRef::new(&pool, 1337); 219 | /// assert_eq!(1337, *number); 220 | /// if let Some(number_ref) = PoolRef::get_mut(&mut number) { 221 | /// *number_ref = 123; 222 | /// } else { 223 | /// panic!("Couldn't get a unique reference!"); 224 | /// } 225 | /// assert_eq!(123, *number); 226 | /// ``` 227 | pub fn get_mut(this: &mut Self) -> Option<&mut A> { 228 | let handle = this.box_ref_mut(); 229 | if handle.is_shared() { 230 | None 231 | } else { 232 | Some(handle.value_as_mut()) 233 | } 234 | } 235 | 236 | /// Attempt to unwrap the value inside a `PoolRef`. 237 | /// 238 | /// If this `PoolRef` isn't the only reference to the value, ownership of 239 | /// the `PoolRef` is passed back to you in the `Err` value. 240 | /// 241 | /// Please note that the unwrapped value is not reclaimed by the pool when 242 | /// dropped. 243 | /// 244 | /// # Examples 245 | /// 246 | /// ```rust 247 | /// # use refpool::{Pool, PoolRef}; 248 | /// let pool: Pool = Pool::new(1); 249 | /// let ref1 = PoolRef::default(&pool); 250 | /// let ref2 = ref1.clone(); 251 | /// let unwrap_result = PoolRef::try_unwrap(ref1); 252 | /// assert!(unwrap_result.is_err()); 253 | /// ``` 254 | /// 255 | /// ```rust 256 | /// # use refpool::{Pool, PoolRef}; 257 | /// let pool: Pool = Pool::new(1); 258 | /// let ref1 = PoolRef::new(&pool, 1337); 259 | /// if let Ok(number) = PoolRef::try_unwrap(ref1) { 260 | /// assert_eq!(1337, number); 261 | /// } else { 262 | /// panic!("couldn't unwrap the number after all!"); 263 | /// } 264 | /// ``` 265 | pub fn try_unwrap(this: Self) -> Result { 266 | if this.box_ref().is_shared() { 267 | Err(this) 268 | } else { 269 | let handle = unsafe { Box::from_raw(this.handle.get_ptr()) }; 270 | std::mem::forget(this); 271 | Ok(handle.value) 272 | } 273 | } 274 | 275 | /// Unwrap the value inside a `PoolRef`, cloning if necessary. 276 | /// 277 | /// If this `PoolRef` is a unique reference to the value, the value is 278 | /// unwrapped and returned, consuming the `PoolRef`. Otherwise, the value 279 | /// is cloned and the clone is returned. 280 | /// 281 | /// Please note that the unwrapped value is not reclaimed by the pool when 282 | /// dropped. 283 | /// 284 | /// # Examples 285 | /// 286 | /// ```rust 287 | /// # use refpool::{Pool, PoolRef}; 288 | /// let pool: Pool = Pool::new(1); 289 | /// let number = PoolRef::new(&pool, 1337); 290 | /// let other_ref = number.clone(); 291 | /// assert_eq!(1337, PoolRef::unwrap_or_clone(other_ref)); 292 | /// ``` 293 | pub fn unwrap_or_clone(this: Self) -> A 294 | where 295 | A: PoolClone, 296 | { 297 | if this.box_ref().is_shared() { 298 | this.deref().clone() 299 | } else { 300 | let handle = unsafe { Box::from_raw(this.handle.get_ptr()) }; 301 | std::mem::forget(this); 302 | handle.value 303 | } 304 | } 305 | 306 | /// Test two `PoolRef`s for pointer equality. 307 | /// 308 | /// # Examples 309 | /// 310 | /// ```rust 311 | /// # use refpool::{Pool, PoolRef}; 312 | /// let pool: Pool = Pool::new(1); 313 | /// let ref1 = PoolRef::default(&pool); 314 | /// let ref2 = ref1.clone(); 315 | /// assert!(PoolRef::ptr_eq(&ref1, &ref2)); 316 | /// ``` 317 | pub fn ptr_eq(left: &Self, right: &Self) -> bool { 318 | std::ptr::eq(left.handle.get_ptr(), right.handle.get_ptr()) 319 | } 320 | 321 | /// Get the current number of `LocalRef` references to the wrapped value. 322 | /// 323 | /// # Examples 324 | /// 325 | /// ```rust 326 | /// # use refpool::{Pool, PoolRef}; 327 | /// let pool: Pool = Pool::new(1); 328 | /// let ref1 = PoolRef::default(&pool); 329 | /// let ref2 = ref1.clone(); 330 | /// let ref3 = ref2.clone(); 331 | /// assert_eq!(3, PoolRef::strong_count(&ref1)); 332 | /// ``` 333 | pub fn strong_count(this: &Self) -> usize { 334 | this.box_ref().count.count() 335 | } 336 | 337 | /// Consume the `PoolRef` and return a pointer to the contents. 338 | /// 339 | /// The pointer is guaranteed to be non-null. 340 | /// 341 | /// Please note that the only proper way to drop the value pointed to 342 | /// is by using `PoolRef::from_raw` to turn it back into a `PoolRef`, because 343 | /// the value is followed by `PoolRef` metadata which also needs to 344 | /// be dropped. 345 | pub fn into_raw(b: PoolRef) -> *const A { 346 | let ptr = b.handle.cast(); 347 | std::mem::forget(b); 348 | ptr.as_ptr() 349 | } 350 | 351 | /// Turn a raw pointer back into a `PoolRef`. 352 | /// 353 | /// The pointer must be non-null and obtained from a previous call to 354 | /// `PoolRef::into_raw` or `PoolRef::into_raw_non_null`. 355 | /// 356 | /// # Safety 357 | /// 358 | /// This must *only* be called on pointers obtained through `PoolRef::into_raw`. 359 | /// It's not OK to call it on a pointer to a value of `A` you've allocated 360 | /// yourself. 361 | /// 362 | /// # Examples 363 | /// 364 | /// ```rust 365 | /// # use refpool::{Pool, PoolRef}; 366 | /// let pool: Pool = Pool::new(1); 367 | /// let ref1 = PoolRef::new(&pool, 31337); 368 | /// 369 | /// // Turn the PoolRef into a raw pointer and see if it still works. 370 | /// let ptr = PoolRef::into_raw(ref1); 371 | /// assert_eq!(31337, unsafe { *ptr }); 372 | /// 373 | /// // Turn it back into a PoolRef and see, again, if it still works. 374 | /// let ref2 = unsafe { PoolRef::from_raw(ptr) }; 375 | /// assert_eq!(31337, *ref2); 376 | /// ``` 377 | pub unsafe fn from_raw(ptr: *const A) -> Self { 378 | Self { 379 | handle: ElementPointer::wrap((ptr as *mut A).cast()), 380 | } 381 | } 382 | 383 | fn box_ref(&self) -> &RefBox { 384 | unsafe { &*self.handle.get_ptr() } 385 | } 386 | 387 | fn box_ref_mut(&mut self) -> &mut RefBox { 388 | unsafe { &mut *self.handle.get_ptr() } 389 | } 390 | } 391 | 392 | impl Drop for PoolRef { 393 | fn drop(&mut self) { 394 | if self.box_ref_mut().dec() != 1 { 395 | return; 396 | } 397 | let handle = unsafe { Box::from_raw(self.handle.get_ptr()) }; 398 | handle.return_to_pool(); 399 | } 400 | } 401 | 402 | impl Clone for PoolRef { 403 | fn clone(&self) -> Self { 404 | let mut new_ref: Self = PoolRef { 405 | handle: ElementPointer::wrap(self.handle.get_ptr()), 406 | }; 407 | new_ref.box_ref_mut().inc(); 408 | new_ref 409 | } 410 | } 411 | 412 | impl Deref for PoolRef { 413 | type Target = A; 414 | fn deref(&self) -> &Self::Target { 415 | self.box_ref().value_as_ref() 416 | } 417 | } 418 | 419 | impl AsRef for PoolRef { 420 | fn as_ref(&self) -> &A { 421 | self.deref() 422 | } 423 | } 424 | 425 | impl Borrow for PoolRef { 426 | fn borrow(&self) -> &A { 427 | self.deref() 428 | } 429 | } 430 | 431 | impl PartialEq for PoolRef 432 | where 433 | A: PartialEq, 434 | { 435 | fn eq(&self, other: &Self) -> bool { 436 | (**self) == (**other) 437 | } 438 | } 439 | 440 | impl Eq for PoolRef where A: Eq {} 441 | 442 | impl PartialOrd for PoolRef 443 | where 444 | A: PartialOrd, 445 | { 446 | fn partial_cmp(&self, other: &Self) -> Option { 447 | (**self).partial_cmp(&**other) 448 | } 449 | } 450 | 451 | impl Ord for PoolRef 452 | where 453 | A: Ord, 454 | { 455 | fn cmp(&self, other: &Self) -> Ordering { 456 | (**self).cmp(&**other) 457 | } 458 | } 459 | 460 | impl Hash for PoolRef 461 | where 462 | A: Hash, 463 | { 464 | fn hash(&self, hasher: &mut H) { 465 | (**self).hash(hasher) 466 | } 467 | } 468 | 469 | impl Display for PoolRef 470 | where 471 | A: Display, 472 | { 473 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 474 | (**self).fmt(f) 475 | } 476 | } 477 | 478 | impl Debug for PoolRef 479 | where 480 | A: Debug, 481 | { 482 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 483 | (**self).fmt(f) 484 | } 485 | } 486 | 487 | impl std::fmt::Pointer for PoolRef { 488 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 489 | std::fmt::Pointer::fmt(&(&**self as *const A), f) 490 | } 491 | } 492 | -------------------------------------------------------------------------------- /src/refbox.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::mem::MaybeUninit; 6 | 7 | use crate::{ 8 | box_handle::PoolBox, counter::Counter, pointer::Pointer, pool::Pool, ref_handle::PoolRef, 9 | types::ElementPointer, 10 | }; 11 | 12 | pub(crate) unsafe fn assume_init(maybe_boxed: Box>) -> Box { 13 | Box::from_raw(Box::into_raw(maybe_boxed).cast()) 14 | // TODO: Change this to `maybe_boxed.assume_init()` when the `new_uninit` 15 | // feature stabilises. 16 | } 17 | 18 | pub(crate) unsafe fn data_ptr(this: &mut MaybeUninit>) -> &mut MaybeUninit { 19 | (*this.as_mut_ptr()) 20 | .value_as_mut_ptr() 21 | .cast::>() 22 | .as_mut() 23 | .unwrap() 24 | } 25 | 26 | #[repr(C)] 27 | pub(crate) struct RefBox { 28 | pub(crate) value: A, 29 | pub(crate) count: usize, 30 | pub(crate) pool: Pool, 31 | } 32 | 33 | impl RefBox { 34 | pub(crate) fn into_ref(mut self: Box) -> PoolRef { 35 | let ref_handle = self.new_ref(); 36 | Box::leak(self); 37 | ref_handle 38 | } 39 | 40 | pub(crate) fn into_box(mut self: Box) -> PoolBox { 41 | let box_handle = self.new_box(); 42 | Box::leak(self); 43 | box_handle 44 | } 45 | 46 | pub(crate) fn new_ref(&mut self) -> PoolRef { 47 | self.inc(); 48 | PoolRef { 49 | handle: ElementPointer::wrap(self), 50 | } 51 | } 52 | 53 | pub(crate) fn new_box(&mut self) -> PoolBox { 54 | self.inc(); 55 | PoolBox { 56 | handle: ElementPointer::wrap(self), 57 | } 58 | } 59 | 60 | pub(crate) fn return_to_pool(self: Box) { 61 | if !self.pool.is_full() { 62 | let pool = self.pool.clone(); 63 | let ptr = Box::into_raw(self); 64 | unsafe { 65 | ptr.drop_in_place(); 66 | pool.push(ElementPointer::wrap(ptr)); 67 | }; 68 | } 69 | } 70 | 71 | pub(crate) fn value_as_ref(&self) -> &A { 72 | &self.value 73 | } 74 | 75 | pub(crate) fn value_as_mut(&mut self) -> &mut A { 76 | &mut self.value 77 | } 78 | 79 | pub(crate) unsafe fn value_as_mut_ptr(&mut self) -> *mut A { 80 | &mut self.value 81 | } 82 | 83 | #[inline(always)] 84 | pub(crate) fn inc(&mut self) { 85 | self.count.inc() 86 | } 87 | 88 | #[inline(always)] 89 | pub(crate) fn dec(&mut self) -> usize { 90 | self.count.dec() 91 | } 92 | 93 | #[inline(always)] 94 | pub(crate) fn is_shared(&self) -> bool { 95 | self.count.count() > 1 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/stack.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | pub(crate) trait Stack { 6 | fn stack_new(max_size: usize) -> Self; 7 | fn stack_push(&mut self, value: A); 8 | fn stack_pop(&mut self) -> Option; 9 | fn stack_len(&self) -> usize; 10 | } 11 | 12 | impl Stack for Vec { 13 | fn stack_new(max_size: usize) -> Self { 14 | Self::with_capacity(max_size) 15 | } 16 | 17 | #[inline(always)] 18 | fn stack_push(&mut self, value: A) { 19 | self.push(value); 20 | } 21 | 22 | #[inline(always)] 23 | fn stack_pop(&mut self) -> Option { 24 | self.pop() 25 | } 26 | 27 | #[inline(always)] 28 | fn stack_len(&self) -> usize { 29 | self.len() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/std_types.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; 6 | use std::hash::{BuildHasher, Hash}; 7 | use std::mem::MaybeUninit; 8 | use std::path::PathBuf; 9 | 10 | use crate::{PoolClone, PoolDefault}; 11 | 12 | /// A marker trait for types which should be fully initialised. 13 | /// 14 | /// Implementing this trait for a type provides a [`PoolDefault`][PoolDefault] 15 | /// implementation which writes the result of 16 | /// [`Default::default()`][Default::default] to its memory location. 17 | /// 18 | /// For types which implement [`Clone`][Clone], this will also provide an 19 | /// implementation of [`PoolClone`][PoolClone] that writes the result of 20 | /// [`Clone::clone()`][Clone::clone] to its memory location. 21 | /// 22 | /// This makes sense for most types, and these implementations are always 23 | /// correct, but you may wish to provide your own implementations for types 24 | /// which don't have to fully initialise their allocated memory regions, which 25 | /// is why we don't implement [`PoolDefault`][PoolDefault] for anything that 26 | /// implements [`Default`][Default] and [`PoolClone`][PoolClone] for anything 27 | /// that implements [`Clone`][Clone], given the absence of [trait 28 | /// specialisation](https://github.com/rust-lang/rust/issues/31844). 29 | /// 30 | /// [PoolDefault]: trait.PoolDefault.html 31 | /// [PoolClone]: trait.PoolClone.html 32 | /// [Default]: https://doc.rust-lang.org/std/default/trait.Default.html 33 | /// [Default::default]: https://doc.rust-lang.org/std/default/trait.Default.html#tymethod.default 34 | /// [Clone]: https://doc.rust-lang.org/std/clone/trait.Clone.html 35 | /// [Clone::clone]: https://doc.rust-lang.org/std/clone/trait.Clone.html#tymethod.clone 36 | pub trait PoolDefaultImpl: Default {} 37 | 38 | impl PoolDefault for A 39 | where 40 | A: PoolDefaultImpl, 41 | { 42 | unsafe fn default_uninit(target: &mut MaybeUninit) { 43 | target.as_mut_ptr().write(Default::default()); 44 | } 45 | } 46 | 47 | impl PoolClone for A 48 | where 49 | A: PoolDefaultImpl + Clone, 50 | { 51 | unsafe fn clone_uninit(&self, target: &mut MaybeUninit) { 52 | target.as_mut_ptr().write(self.clone()); 53 | } 54 | } 55 | 56 | impl PoolDefaultImpl for bool {} 57 | 58 | impl PoolDefaultImpl for u8 {} 59 | impl PoolDefaultImpl for u16 {} 60 | impl PoolDefaultImpl for u32 {} 61 | impl PoolDefaultImpl for u64 {} 62 | impl PoolDefaultImpl for u128 {} 63 | impl PoolDefaultImpl for usize {} 64 | 65 | impl PoolDefaultImpl for i8 {} 66 | impl PoolDefaultImpl for i16 {} 67 | impl PoolDefaultImpl for i32 {} 68 | impl PoolDefaultImpl for i64 {} 69 | impl PoolDefaultImpl for i128 {} 70 | impl PoolDefaultImpl for isize {} 71 | 72 | impl PoolDefaultImpl for Option {} 73 | 74 | impl PoolDefaultImpl for String {} 75 | impl PoolDefaultImpl for PathBuf {} 76 | 77 | impl PoolDefaultImpl for Vec {} 78 | impl PoolDefaultImpl for VecDeque {} 79 | impl PoolDefaultImpl for HashSet {} 80 | impl PoolDefaultImpl for HashMap {} 81 | impl PoolDefaultImpl for BTreeMap {} 82 | impl PoolDefaultImpl for BTreeSet {} 83 | impl PoolDefaultImpl for BinaryHeap {} 84 | impl PoolDefaultImpl for LinkedList {} 85 | 86 | impl PoolDefaultImpl for (A, B) 87 | where 88 | A: PoolDefaultImpl, 89 | B: PoolDefaultImpl, 90 | { 91 | } 92 | 93 | impl PoolDefaultImpl for (A, B, C) 94 | where 95 | A: PoolDefaultImpl, 96 | B: PoolDefaultImpl, 97 | C: PoolDefaultImpl, 98 | { 99 | } 100 | 101 | impl PoolDefaultImpl for (A, B, C, D) 102 | where 103 | A: PoolDefaultImpl, 104 | B: PoolDefaultImpl, 105 | C: PoolDefaultImpl, 106 | D: PoolDefaultImpl, 107 | { 108 | } 109 | 110 | impl PoolDefaultImpl for (A, B, C, D, E) 111 | where 112 | A: PoolDefaultImpl, 113 | B: PoolDefaultImpl, 114 | C: PoolDefaultImpl, 115 | D: PoolDefaultImpl, 116 | E: PoolDefaultImpl, 117 | { 118 | } 119 | 120 | impl PoolDefaultImpl for (A, B, C, D, E, F) 121 | where 122 | A: PoolDefaultImpl, 123 | B: PoolDefaultImpl, 124 | C: PoolDefaultImpl, 125 | D: PoolDefaultImpl, 126 | E: PoolDefaultImpl, 127 | F: PoolDefaultImpl, 128 | { 129 | } 130 | 131 | impl PoolDefaultImpl for (A, B, C, D, E, F, G) 132 | where 133 | A: PoolDefaultImpl, 134 | B: PoolDefaultImpl, 135 | C: PoolDefaultImpl, 136 | D: PoolDefaultImpl, 137 | E: PoolDefaultImpl, 138 | F: PoolDefaultImpl, 139 | G: PoolDefaultImpl, 140 | { 141 | } 142 | 143 | impl PoolDefaultImpl for (A, B, C, D, E, F, G, H) 144 | where 145 | A: PoolDefaultImpl, 146 | B: PoolDefaultImpl, 147 | C: PoolDefaultImpl, 148 | D: PoolDefaultImpl, 149 | E: PoolDefaultImpl, 150 | F: PoolDefaultImpl, 151 | G: PoolDefaultImpl, 152 | H: PoolDefaultImpl, 153 | { 154 | } 155 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | use crate::pool::PoolInner; 2 | use crate::refbox::RefBox; 3 | use std::ptr::NonNull; 4 | 5 | pub(crate) type ElementPointer = NonNull>; 6 | pub(crate) type PoolPointer = NonNull>; 7 | --------------------------------------------------------------------------------