├── .github └── workflows │ └── rust.yml ├── .gitignore ├── .travis.yml ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── src ├── internal.rs ├── lib.rs └── node.rs ├── tests ├── all │ ├── main.rs │ ├── multiple.rs │ ├── quickcheck.rs │ ├── single.rs │ └── test.rs └── multiple.rs └── wasm ├── Cargo.lock ├── Cargo.toml ├── build.sh └── src └── lib.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | test: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Build and Run tests 18 | run: cargo test --verbose 19 | 20 | miri: 21 | runs-on: ubuntu-latest 22 | env: 23 | MIRIFLAGS: -Zmiri-permissive-provenance -Zmiri-disable-isolation -Zmiri-backtrace=full 24 | CARGO_NEXTEST_VERSION: 0.9.88 25 | steps: 26 | - uses: actions/checkout@v4 27 | 28 | # Install Rust nightly and MIRI. 29 | - run: rustup toolchain install nightly 30 | - run: rustup default nightly 31 | - run: rustup component add rust-src miri 32 | 33 | # Install and cache `cargo-nextest`. 34 | - uses: actions/cache@v4 35 | with: 36 | path: ${{ runner.tool_cache }}/cargo-nextest 37 | key: cargo-nextest-bin-${{ env.CARGO_NEXTEST_VERSION }} 38 | - run: echo "${{ runner.tool_cache }}/cargo-nextest/bin" >> $GITHUB_PATH 39 | - run: cargo install --root "${{ runner.tool_cache }}/cargo-nextest" --version ${{ env.CARGO_NEXTEST_VERSION }} cargo-nextest --locked 40 | 41 | - run: cargo miri nextest run -j4 --no-fail-fast 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: cargo 3 | 4 | rust: 5 | - stable 6 | - beta 7 | - nightly 8 | 9 | script: 10 | - cargo test 11 | - if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then ./wasm/build.sh; fi 12 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "0.6.4" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "atty" 16 | version = "0.2.14" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 19 | dependencies = [ 20 | "hermit-abi", 21 | "libc", 22 | "winapi", 23 | ] 24 | 25 | [[package]] 26 | name = "bumpalo" 27 | version = "3.16.0" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 30 | 31 | [[package]] 32 | name = "env_logger" 33 | version = "0.5.6" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "0561146661ae44c579e993456bc76d11ce1e0c7d745e57b2fa7146b6e49fa2ad" 36 | dependencies = [ 37 | "atty", 38 | "humantime", 39 | "log", 40 | "regex", 41 | "termcolor", 42 | ] 43 | 44 | [[package]] 45 | name = "fuchsia-cprng" 46 | version = "0.1.1" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 49 | 50 | [[package]] 51 | name = "hermit-abi" 52 | version = "0.1.19" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 55 | dependencies = [ 56 | "libc", 57 | ] 58 | 59 | [[package]] 60 | name = "humantime" 61 | version = "1.3.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 64 | dependencies = [ 65 | "quick-error", 66 | ] 67 | 68 | [[package]] 69 | name = "intrusive_splay_tree" 70 | version = "0.2.3" 71 | dependencies = [ 72 | "bumpalo", 73 | "quickcheck", 74 | ] 75 | 76 | [[package]] 77 | name = "lazy_static" 78 | version = "1.5.0" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 81 | 82 | [[package]] 83 | name = "libc" 84 | version = "0.2.169" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" 87 | 88 | [[package]] 89 | name = "log" 90 | version = "0.4.22" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 93 | 94 | [[package]] 95 | name = "memchr" 96 | version = "2.7.4" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 99 | 100 | [[package]] 101 | name = "quick-error" 102 | version = "1.2.3" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 105 | 106 | [[package]] 107 | name = "quickcheck" 108 | version = "0.6.2" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "c01babc5ffd48a2a83744b3024814bb46dfd4f2a4705ccb44b1b60e644fdcab7" 111 | dependencies = [ 112 | "env_logger", 113 | "log", 114 | "rand", 115 | ] 116 | 117 | [[package]] 118 | name = "rand" 119 | version = "0.4.6" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" 122 | dependencies = [ 123 | "fuchsia-cprng", 124 | "libc", 125 | "rand_core 0.3.1", 126 | "rdrand", 127 | "winapi", 128 | ] 129 | 130 | [[package]] 131 | name = "rand_core" 132 | version = "0.3.1" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 135 | dependencies = [ 136 | "rand_core 0.4.2", 137 | ] 138 | 139 | [[package]] 140 | name = "rand_core" 141 | version = "0.4.2" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 144 | 145 | [[package]] 146 | name = "rdrand" 147 | version = "0.4.0" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 150 | dependencies = [ 151 | "rand_core 0.3.1", 152 | ] 153 | 154 | [[package]] 155 | name = "regex" 156 | version = "0.2.10" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "aec3f58d903a7d2a9dc2bf0e41a746f4530e0cab6b615494e058f67a3ef947fb" 159 | dependencies = [ 160 | "aho-corasick", 161 | "memchr", 162 | "regex-syntax", 163 | "thread_local", 164 | "utf8-ranges", 165 | ] 166 | 167 | [[package]] 168 | name = "regex-syntax" 169 | version = "0.5.3" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "b2550876c31dc914696a6c2e01cbce8afba79a93c8ae979d2fe051c0230b3756" 172 | dependencies = [ 173 | "ucd-util", 174 | ] 175 | 176 | [[package]] 177 | name = "termcolor" 178 | version = "0.3.6" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83" 181 | dependencies = [ 182 | "wincolor", 183 | ] 184 | 185 | [[package]] 186 | name = "thread_local" 187 | version = "0.3.6" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" 190 | dependencies = [ 191 | "lazy_static", 192 | ] 193 | 194 | [[package]] 195 | name = "ucd-util" 196 | version = "0.1.1" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" 199 | 200 | [[package]] 201 | name = "utf8-ranges" 202 | version = "1.0.0" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" 205 | 206 | [[package]] 207 | name = "winapi" 208 | version = "0.3.9" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 211 | dependencies = [ 212 | "winapi-i686-pc-windows-gnu", 213 | "winapi-x86_64-pc-windows-gnu", 214 | ] 215 | 216 | [[package]] 217 | name = "winapi-i686-pc-windows-gnu" 218 | version = "0.4.0" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 221 | 222 | [[package]] 223 | name = "winapi-x86_64-pc-windows-gnu" 224 | version = "0.4.0" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 227 | 228 | [[package]] 229 | name = "wincolor" 230 | version = "0.1.6" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767" 233 | dependencies = [ 234 | "winapi", 235 | ] 236 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Nick Fitzgerald "] 3 | categories = ["memory-management", "data-structures", "no-std", "wasm"] 4 | description = "An intrusive splay tree implementation that is no-std compatible and free from allocation and moves." 5 | edition = "2021" 6 | license = "MPL-2.0" 7 | name = "intrusive_splay_tree" 8 | readme = "./README.md" 9 | repository = "https://github.com/fitzgen/intrusive_splay_tree" 10 | version = "0.2.3" 11 | 12 | [dev-dependencies] 13 | bumpalo = "3.16.0" 14 | quickcheck = "0.6.2" 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

intrusive_splay_tree

3 |

An intrusive, allocation-free splay tree implementation.

4 |

5 | 6 | 7 | 8 | 9 |

10 |
11 | 12 | [Splay trees] are self-adjusting, meaning that operating on an element (for 13 | example, doing a `find` or an `insert`) rebalances the tree in such a way 14 | that the element becomes the root. This means that subsequent operations on 15 | that element are *O(1)* as long as no other element is operated on in the 16 | meantime. 17 | 18 | ### Implementation and Goals 19 | 20 | * **Intrusive:** The space for the subtree pointers is stored *inside* the 21 | element type. In non-intrusive trees, we would have a node type that 22 | contains the subtree pointers and either a pointer to the element or we 23 | would move the element into the node. The intrusive design inverts the 24 | relationship, so that the elements hold the subtree pointers within 25 | themselves. 26 | 27 | * **Freedom from allocations and moves:** The intrusive design enables this 28 | implementation to fully avoid both allocations and moving elements in 29 | memory. Since the space for subtree pointers already exists in the element, 30 | no allocation is necessary, just a handful of pointer writes. Therefore, 31 | this implementation can be used in constrained environments that don't have 32 | access to an allocator (e.g. some embedded devices or within a signal 33 | handler) and with types that can't move in memory (e.g. `pthread_mutex_t`). 34 | 35 | * **Small code size:** This implementation is geared towards small code 36 | size, and uses trait objects internally to avoid the code bloat induced by 37 | monomorphization. This implementation is suitable for targeting WebAssembly, 38 | where code is downloaded over the network, and code bloat delays Web page 39 | loading. 40 | 41 | * **Nodes do not have parent pointers**: An intrusive node is only two words 42 | in size: left and right sub tree pointers. There are no parent pointers, 43 | which would require another word of overhead. To meet this goal, the 44 | implementation uses the "top-down" variant of splay trees. 45 | 46 | [Splay trees]: https://en.wikipedia.org/wiki/Splay_tree 47 | [paper]: http://www.cs.cmu.edu/~sleator/papers/self-adjusting.pdf 48 | 49 | ### Constraints 50 | 51 | * **Elements within a tree must all have the same lifetime.** This means 52 | that you must use something like the [`bumpalo`][arena] crate for 53 | allocation, or be working with static data, etc. 54 | 55 | * **Elements in an intrusive collections are inherently shared.** They are 56 | always potentially aliased by the collection(s) they are in. In the other 57 | direction, a particular intrusive collection only has a shared reference to 58 | the element, since elements can both be in many intrusive collections at the 59 | same time. Therefore, you cannot get a unique, mutable reference to an 60 | element out of an intrusive splay tree. To work around this, you may need to 61 | liberally use interior mutability, for example by leveraging `Cell`, 62 | `RefCell`, and `Mutex`. 63 | 64 | [arena]: https://crates.io/crates/bumpalo 65 | 66 | ### Example 67 | 68 | This example defines a `Monster` type, where each of its instances live 69 | within two intrusive trees: one ordering monsters by their name, and the 70 | other ordering them by their health. 71 | 72 | ```rust 73 | use intrusive_splay_tree::{impl_intrusive_node, SplayTree}; 74 | 75 | use std::cmp::Ordering; 76 | use std::marker::PhantomData; 77 | 78 | // We have a monster type, and we want to query monsters by both name and 79 | // health. 80 | #[derive(Debug)] 81 | struct Monster<'a> { 82 | name: String, 83 | health: u64, 84 | 85 | // An intrusive node so we can put monsters in a tree to query by name. 86 | by_name_node: intrusive_splay_tree::Node<'a>, 87 | 88 | // Another intrusive node so we can put monsters in a second tree (at 89 | // the same time!) and query them by health. 90 | by_health_node: intrusive_splay_tree::Node<'a>, 91 | } 92 | 93 | // Define a type for trees where monsters are ordered by name. 94 | struct MonstersByName; 95 | 96 | // Implement `IntrusiveNode` for the `MonstersByName` tree, where the 97 | // element type is `Monster` and the field in `Monster` that has this tree's 98 | // intrusive node is `by_name`. 99 | impl_intrusive_node! { 100 | impl<'a> IntrusiveNode<'a> for MonstersByName 101 | where 102 | type Elem = Monster<'a>, 103 | node = by_name_node; 104 | } 105 | 106 | // Define how to order `Monster`s within the `MonstersByName` tree by 107 | // implementing `TreeOrd`. 108 | impl<'a> intrusive_splay_tree::TreeOrd<'a, MonstersByName> for Monster<'a> { 109 | fn tree_cmp(&self, rhs: &Monster<'a>) -> Ordering { 110 | self.name.cmp(&rhs.name) 111 | } 112 | } 113 | 114 | // And do all the same things for trees where monsters are ordered by health... 115 | struct MonstersByHealth; 116 | impl_intrusive_node! { 117 | impl<'a> IntrusiveNode<'a> for MonstersByHealth 118 | where 119 | type Elem = Monster<'a>, 120 | node = by_health_node; 121 | } 122 | impl<'a> intrusive_splay_tree::TreeOrd<'a, MonstersByHealth> for Monster<'a> { 123 | fn tree_cmp(&self, rhs: &Monster<'a>) -> Ordering { 124 | self.health.cmp(&rhs.health) 125 | } 126 | } 127 | 128 | // We can also implement `TreeOrd` for other types, so that we can query the 129 | // tree by these types. For example, we want to query the `MonstersByHealth` 130 | // tree by some `u64` health value, and we want to query the `MonstersByName` 131 | // tree by some `&str` name value. 132 | 133 | impl<'a> intrusive_splay_tree::TreeOrd<'a, MonstersByHealth> for u64 { 134 | fn tree_cmp(&self, rhs: &Monster<'a>) -> Ordering { 135 | self.cmp(&rhs.health) 136 | } 137 | } 138 | 139 | impl<'a> intrusive_splay_tree::TreeOrd<'a, MonstersByName> for str { 140 | fn tree_cmp(&self, rhs: &Monster<'a>) -> Ordering { 141 | self.cmp(&rhs.name) 142 | } 143 | } 144 | 145 | impl<'a> Monster<'a> { 146 | /// The `Monster` constructor allocates `Monster`s in a bump arena, and 147 | /// inserts the new `Monster` in both trees. 148 | pub fn new( 149 | arena: &'a bumpalo::Bump, 150 | name: String, 151 | health: u64, 152 | by_name_tree: &mut SplayTree<'a, MonstersByName>, 153 | by_health_tree: &mut SplayTree<'a, MonstersByHealth> 154 | ) -> &'a Monster<'a> { 155 | let monster = arena.alloc(Monster { 156 | name, 157 | health, 158 | by_name_node: Default::default(), 159 | by_health_node: Default::default(), 160 | }); 161 | 162 | by_name_tree.insert(monster); 163 | by_health_tree.insert(monster); 164 | 165 | monster 166 | } 167 | } 168 | 169 | fn main() { 170 | // The arena that the monsters will live within. 171 | let mut arena = bumpalo::Bump::new(); 172 | 173 | // The splay trees ordered by name and health respectively. 174 | let mut by_name_tree = SplayTree::default(); 175 | let mut by_health_tree = SplayTree::default(); 176 | 177 | // Now let's create some monsters, inserting them into the trees! 178 | 179 | Monster::new( 180 | &arena, 181 | "Frankenstein's Monster".into(), 182 | 99, 183 | &mut by_name_tree, 184 | &mut by_health_tree, 185 | ); 186 | 187 | Monster::new( 188 | &arena, 189 | "Godzilla".into(), 190 | 2000, 191 | &mut by_name_tree, 192 | &mut by_health_tree, 193 | ); 194 | 195 | Monster::new( 196 | &arena, 197 | "Vegeta".into(), 198 | 9001, 199 | &mut by_name_tree, 200 | &mut by_health_tree, 201 | ); 202 | 203 | // Query the `MonstersByName` tree by a name. 204 | 205 | let godzilla = by_name_tree.find("Godzilla").unwrap(); 206 | assert_eq!(godzilla.name, "Godzilla"); 207 | 208 | assert!(by_name_tree.find("Gill-Man").is_none()); 209 | 210 | // Query the `MonstersByHealth` tree by a health. 211 | 212 | let vegeta = by_health_tree.find(&9001).unwrap(); 213 | assert_eq!(vegeta.name, "Vegeta"); 214 | 215 | assert!(by_health_tree.find(&0).is_none()); 216 | } 217 | ``` 218 | -------------------------------------------------------------------------------- /src/internal.rs: -------------------------------------------------------------------------------- 1 | //! The actual splay tree implementation. 2 | //! 3 | //! This implementation has no generics, works only with trait objects, and 4 | //! therefore does no monomorphization. While the `pub struct SplayTree` 5 | //! users' API does use generics for ergonomics, it immediately erases types by 6 | //! converting them to trait objects before calling into this `internal` 7 | //! implementation. By erasing generic types, we keep code size 8 | //! small. Therefore, it doesn't make sense to allow any of the `internal` 9 | //! methods working with trait objects to be inlined, or else all our work would 10 | //! be undone. 11 | 12 | use super::Node; 13 | use core::cmp; 14 | 15 | /// Internal trait for anything that can be compared to a `Node`. 16 | pub trait CompareToNode<'a> { 17 | /// Compare `self` to the value containing the given `Node`. 18 | /// 19 | /// # Safety 20 | /// 21 | /// Unsafe because implementers rely on only being called with nodes 22 | /// contained within the `NodeOffset::Value` container type they are 23 | /// expecting, and if given a random `Node`, then calling this will lead 24 | /// to unsafety. 25 | unsafe fn compare_to_node(&self, node: &'a Node<'a>) -> cmp::Ordering; 26 | } 27 | 28 | /// A node comparator to get the minimum node. 29 | struct MinNode; 30 | impl<'a> CompareToNode<'a> for MinNode { 31 | unsafe fn compare_to_node(&self, _node: &'a Node<'a>) -> cmp::Ordering { 32 | cmp::Ordering::Less 33 | } 34 | } 35 | 36 | /// A node comparator to get the maximum node. 37 | struct MaxNode; 38 | impl<'a> CompareToNode<'a> for MaxNode { 39 | unsafe fn compare_to_node(&self, _node: &'a Node<'a>) -> cmp::Ordering { 40 | cmp::Ordering::Greater 41 | } 42 | } 43 | 44 | #[derive(Debug)] 45 | pub struct SplayTree<'a> { 46 | root: Option<&'a Node<'a>>, 47 | } 48 | 49 | impl<'a> Default for SplayTree<'a> { 50 | #[inline] 51 | fn default() -> SplayTree<'a> { 52 | SplayTree { root: None } 53 | } 54 | } 55 | 56 | impl<'a> SplayTree<'a> { 57 | #[inline] 58 | pub const fn new() -> Self { 59 | SplayTree { root: None } 60 | } 61 | 62 | #[inline] 63 | pub fn is_empty(&self) -> bool { 64 | self.root.is_none() 65 | } 66 | 67 | #[inline] 68 | pub fn root(&self) -> Option<&'a Node<'a>> { 69 | self.root 70 | } 71 | 72 | #[inline(never)] 73 | pub unsafe fn find(&mut self, key: &dyn CompareToNode<'a>) -> Option<&'a Node<'a>> { 74 | match self.root { 75 | Some(root) => { 76 | let root = self.splay(root, key); 77 | if let cmp::Ordering::Equal = key.compare_to_node(root) { 78 | Some(root) 79 | } else { 80 | None 81 | } 82 | } 83 | None => None, 84 | } 85 | } 86 | 87 | #[inline(never)] 88 | pub unsafe fn insert(&mut self, key: &dyn CompareToNode<'a>, node: &'a Node<'a>) -> bool { 89 | debug_assert!(node.left.get().is_none() && node.right.get().is_none()); 90 | 91 | match self.root { 92 | Some(root) => { 93 | let root = self.splay(root, key); 94 | 95 | match key.compare_to_node(root) { 96 | cmp::Ordering::Equal => return false, 97 | cmp::Ordering::Less => { 98 | node.left.set(root.left.get()); 99 | node.right.set(Some(root)); 100 | root.left.set(None); 101 | } 102 | cmp::Ordering::Greater => { 103 | node.right.set(root.right.get()); 104 | node.left.set(Some(root)); 105 | root.right.set(None); 106 | } 107 | } 108 | 109 | self.root = Some(node); 110 | true 111 | } 112 | None => { 113 | self.root = Some(node); 114 | true 115 | } 116 | } 117 | } 118 | 119 | #[inline] 120 | pub fn min(&mut self) -> Option<&'a Node<'a>> { 121 | let root = self.root()?; 122 | Some(unsafe { self.splay(root, &MinNode) }) 123 | } 124 | 125 | #[inline] 126 | pub fn pop_min(&mut self) -> Option<&'a Node<'a>> { 127 | self.min()?; 128 | self.pop_root() 129 | } 130 | 131 | #[inline] 132 | pub fn max(&mut self) -> Option<&'a Node<'a>> { 133 | let root = self.root()?; 134 | Some(unsafe { self.splay(root, &MaxNode) }) 135 | } 136 | 137 | #[inline] 138 | pub fn pop_max(&mut self) -> Option<&'a Node<'a>> { 139 | self.max()?; 140 | self.pop_root() 141 | } 142 | 143 | pub fn pop_root(&mut self) -> Option<&'a Node<'a>> { 144 | let old_root = self.root.take()?; 145 | 146 | match old_root.left.get() { 147 | Some(old_root_left) => { 148 | let old_root_right = old_root.right.get(); 149 | unsafe { 150 | self.splay(old_root_left, &MaxNode) 151 | .right 152 | .set(old_root_right) 153 | } 154 | } 155 | None => { 156 | self.root = old_root.right.get(); 157 | } 158 | } 159 | 160 | old_root.left.set(None); 161 | old_root.right.set(None); 162 | Some(old_root) 163 | } 164 | 165 | pub unsafe fn remove(&mut self, key: &dyn CompareToNode<'a>) -> Option<&'a Node<'a>> { 166 | let root = self.root?; 167 | self.splay(root, key); 168 | if self.root.is_some_and(|r| key.compare_to_node(r).is_eq()) { 169 | self.pop_root() 170 | } else { 171 | None 172 | } 173 | } 174 | 175 | #[inline] 176 | pub fn walk(&self, f: &mut dyn FnMut(&'a Node<'a>) -> bool) { 177 | if let Some(root) = self.root { 178 | root.walk(f); 179 | } 180 | } 181 | 182 | // The "simple top-down splay" routine from the paper. 183 | #[inline(never)] 184 | unsafe fn splay( 185 | &mut self, 186 | mut current: &'a Node<'a>, 187 | key: &dyn CompareToNode<'a>, 188 | ) -> &'a Node<'a> { 189 | let null = Node::default(); 190 | let mut left = &null; 191 | let mut right = &null; 192 | 193 | loop { 194 | match key.compare_to_node(current) { 195 | cmp::Ordering::Less => { 196 | match current.left.get() { 197 | None => break, 198 | Some(mut current_left) => { 199 | if let cmp::Ordering::Less = key.compare_to_node(current_left) { 200 | // Rotate right. 201 | current.left.set(current_left.right.get()); 202 | current_left.right.set(Some(current)); 203 | current = current_left; 204 | match current.left.get() { 205 | Some(l) => current_left = l, 206 | None => break, 207 | } 208 | } 209 | // Link right. 210 | right.left.set(Some(current)); 211 | right = current; 212 | current = current_left; 213 | } 214 | } 215 | } 216 | cmp::Ordering::Greater => { 217 | match current.right.get() { 218 | None => break, 219 | Some(mut current_right) => { 220 | if let cmp::Ordering::Greater = key.compare_to_node(current_right) { 221 | // Rotate left. 222 | current.right.set(current_right.left.get()); 223 | current_right.left.set(Some(current)); 224 | current = current_right; 225 | match current_right.right.get() { 226 | Some(r) => current_right = r, 227 | None => break, 228 | } 229 | } 230 | // Link left. 231 | left.right.set(Some(current)); 232 | left = current; 233 | current = current_right; 234 | } 235 | } 236 | } 237 | cmp::Ordering::Equal => break, 238 | } 239 | } 240 | 241 | // Assemble. 242 | left.right.set(current.left.get()); 243 | right.left.set(current.right.get()); 244 | current.left.set(null.right.get()); 245 | current.right.set(null.left.get()); 246 | self.root = Some(current); 247 | current 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | #![deny(missing_docs)] 3 | #![deny(missing_debug_implementations)] 4 | #![no_std] 5 | 6 | mod internal; 7 | mod node; 8 | 9 | pub use node::Node; 10 | 11 | use core::cmp; 12 | use core::fmt; 13 | use core::iter; 14 | use core::marker::PhantomData; 15 | 16 | /// Defines how to get the intrusive node from a particular kind of 17 | /// `SplayTree`'s element type. 18 | /// 19 | /// Don't implement this by hand -- doing so is both boring and dangerous! 20 | /// Instead, use the `impl_intrusive_node!` macro. 21 | pub unsafe trait IntrusiveNode<'a> 22 | where 23 | Self: Sized, 24 | { 25 | /// The element struct type that contains a node for this tree. 26 | type Elem: TreeOrd<'a, Self>; 27 | 28 | /// Get the node for this tree from the given element. 29 | fn elem_to_node(elem: &'a Self::Elem) -> &'a Node<'a>; 30 | 31 | /// Get the element for this node (by essentially doing `offsetof` the 32 | /// node's field). 33 | /// 34 | /// ## Safety 35 | /// 36 | /// Given a node inside a different element type, or a node for a different 37 | /// tree within the same element type, this method will result in memory 38 | /// unsafety. 39 | #[doc(hidden)] 40 | unsafe fn node_to_elem(node: &'a Node<'a>) -> &'a Self::Elem; 41 | } 42 | 43 | /// Implement `IntrusiveNode` for a particular kind of `SplayTree` and its 44 | /// element type. 45 | #[macro_export] 46 | macro_rules! impl_intrusive_node { 47 | ( 48 | impl< $($typarams:tt),* > 49 | IntrusiveNode<$intrusive_node_lifetime:tt> 50 | for $tree:ty 51 | where 52 | type Elem = $elem:ty , 53 | node = $node:ident ; 54 | ) => { 55 | unsafe impl< $( $typarams )* > $crate::IntrusiveNode<$intrusive_node_lifetime> for $tree { 56 | type Elem = $elem; 57 | 58 | fn elem_to_node( 59 | elem: & $intrusive_node_lifetime Self::Elem 60 | ) -> & $intrusive_node_lifetime $crate::Node< $intrusive_node_lifetime > { 61 | &elem.$node 62 | } 63 | 64 | unsafe fn node_to_elem( 65 | node: & $intrusive_node_lifetime $crate::Node< $intrusive_node_lifetime > 66 | ) -> & $intrusive_node_lifetime Self::Elem { 67 | let node = core::ptr::with_exposed_provenance::(node as *const _ as usize); 68 | 69 | let offset = core::mem::offset_of!(Self::Elem, $node); 70 | let offset = isize::try_from(offset).unwrap(); 71 | let neg_offset = offset.checked_neg().unwrap(); 72 | 73 | let elem = node.offset(neg_offset); 74 | let elem = elem.cast::(); 75 | 76 | elem.as_ref().unwrap() 77 | } 78 | } 79 | } 80 | } 81 | 82 | /// A total ordering between the `Self` type and the tree's element type 83 | /// `T::Elem`. 84 | /// 85 | /// Different from `Ord` in that it allows `Self` and `T::Elem` to be distinct 86 | /// types, so that you can query a splay tree without fully constructing its 87 | /// element type. 88 | pub trait TreeOrd<'a, T: IntrusiveNode<'a>> { 89 | /// What is the ordering relationship between `self` and the given tree 90 | /// element? 91 | fn tree_cmp(&self, elem: &'a T::Elem) -> cmp::Ordering; 92 | } 93 | 94 | struct Query<'a, 'b, K, T> 95 | where 96 | T: 'a + IntrusiveNode<'a>, 97 | K: 'b + ?Sized + TreeOrd<'a, T>, 98 | { 99 | key: &'b K, 100 | _phantom: PhantomData<&'a T>, 101 | } 102 | 103 | impl<'a, 'b, K, T> Query<'a, 'b, K, T> 104 | where 105 | T: IntrusiveNode<'a>, 106 | K: 'b + ?Sized + TreeOrd<'a, T>, 107 | { 108 | #[inline] 109 | fn new(key: &'b K) -> Query<'a, 'b, K, T> { 110 | Query { 111 | key, 112 | _phantom: PhantomData, 113 | } 114 | } 115 | } 116 | 117 | impl<'a, 'b, K, T> internal::CompareToNode<'a> for Query<'a, 'b, K, T> 118 | where 119 | T: 'a + IntrusiveNode<'a>, 120 | T::Elem: 'a, 121 | K: 'b + ?Sized + TreeOrd<'a, T>, 122 | { 123 | #[inline] 124 | unsafe fn compare_to_node(&self, node: &'a Node<'a>) -> cmp::Ordering { 125 | let val = T::node_to_elem(node); 126 | self.key.tree_cmp(val) 127 | } 128 | } 129 | 130 | /// An intrusive splay tree. 131 | /// 132 | /// The tree is parameterized by some marker type `T` whose `IntrusiveNode` 133 | /// implementation defines: 134 | /// 135 | /// * the element type contained in this tree: `T::Elem`, 136 | /// * how to get the intrusive node for this tree within an element, 137 | /// * and how to get the container element from a given intrusive node for this 138 | /// tree. 139 | pub struct SplayTree<'a, T> 140 | where 141 | T: IntrusiveNode<'a>, 142 | T::Elem: 'a, 143 | { 144 | tree: internal::SplayTree<'a>, 145 | _phantom: PhantomData<&'a T::Elem>, 146 | } 147 | 148 | impl<'a, T> Default for SplayTree<'a, T> 149 | where 150 | T: 'a + IntrusiveNode<'a>, 151 | T::Elem: 'a, 152 | { 153 | #[inline] 154 | fn default() -> SplayTree<'a, T> { 155 | SplayTree { 156 | tree: internal::SplayTree::default(), 157 | _phantom: PhantomData, 158 | } 159 | } 160 | } 161 | 162 | impl<'a, T> fmt::Debug for SplayTree<'a, T> 163 | where 164 | T: 'a + IntrusiveNode<'a>, 165 | T::Elem: 'a + fmt::Debug, 166 | { 167 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 168 | let set = &mut f.debug_set(); 169 | self.walk(|x| { 170 | set.entry(x); 171 | }); 172 | set.finish() 173 | } 174 | } 175 | 176 | impl<'a, T> Extend<&'a T::Elem> for SplayTree<'a, T> 177 | where 178 | T: 'a + IntrusiveNode<'a>, 179 | { 180 | #[inline] 181 | fn extend>(&mut self, iter: I) { 182 | for x in iter { 183 | self.insert(x); 184 | } 185 | } 186 | } 187 | 188 | impl<'a, T> iter::FromIterator<&'a T::Elem> for SplayTree<'a, T> 189 | where 190 | T: 'a + IntrusiveNode<'a>, 191 | T::Elem: fmt::Debug, 192 | { 193 | #[inline] 194 | fn from_iter>(iter: I) -> Self { 195 | let mut me = SplayTree::default(); 196 | me.extend(iter); 197 | me 198 | } 199 | } 200 | 201 | impl<'a, T> SplayTree<'a, T> 202 | where 203 | T: 'a + IntrusiveNode<'a>, 204 | { 205 | /// Construct a new, empty tree. 206 | #[inline] 207 | pub const fn new() -> Self { 208 | Self { 209 | tree: internal::SplayTree::new(), 210 | _phantom: PhantomData, 211 | } 212 | } 213 | 214 | /// Is this tree empty? 215 | #[inline] 216 | pub fn is_empty(&self) -> bool { 217 | self.tree.is_empty() 218 | } 219 | 220 | /// Get a reference to the root element, if any exists. 221 | pub fn root(&self) -> Option<&'a T::Elem> { 222 | self.tree.root().map(|r| unsafe { T::node_to_elem(r) }) 223 | } 224 | 225 | /// Find an element in the tree. 226 | /// 227 | /// This operation will splay the queried element to the root of the tree. 228 | /// 229 | /// The `key` must be of a type that implements `TreeOrd` for this tree's 230 | /// `T` type. The element type `T::Elem` must always implement `TreeOrd`, 231 | /// so you can search the tree by element. You can also implement 232 | /// `TreeOrd` for additional key types. This allows you to search the 233 | /// tree without constructing a full element. 234 | #[inline] 235 | pub fn find(&mut self, key: &K) -> Option<&'a T::Elem> 236 | where 237 | K: ?Sized + TreeOrd<'a, T>, 238 | { 239 | unsafe { 240 | let query: Query<_, T> = Query::new(key); 241 | self.tree.find(&query).map(|node| T::node_to_elem(node)) 242 | } 243 | } 244 | 245 | /// Insert a new element into this tree. 246 | /// 247 | /// Returns `true` if the element was inserted into the tree. 248 | /// 249 | /// Returns `false` if there was already an element in the tree for which 250 | /// `TreeOrd` returned `Ordering::Equal`. In this case, the extant element 251 | /// is left in the tree, and `elem` is not inserted. 252 | /// 253 | /// This operation will splay the inserted element to the root of the tree. 254 | /// 255 | /// It is a logic error to insert an element that is already inserted in a 256 | /// `T` tree. 257 | /// 258 | /// ## Panics 259 | /// 260 | /// If `debug_assertions` are enabled, then this function may panic if 261 | /// `elem` is already in a `T` tree. If `debug_assertions` are not defined, 262 | /// the behavior is safe, but unspecified. 263 | #[inline] 264 | pub fn insert(&mut self, elem: &'a T::Elem) -> bool { 265 | // To satisfy MIRI, we need to expose provenance of element added to the 266 | // tree, so that when we query the tree and go from node-to-elem, we can 267 | // use this exposed provenance. This is because, while the lifetimes 268 | // ensure that the element remains borrowed while inserted in the tree, 269 | // we don't have a good way to plumb through a pointer with the original 270 | // element's borrowed provenance through to all node-to-elem 271 | // conversions. 272 | let _ = (elem as *const T::Elem).expose_provenance(); 273 | 274 | unsafe { 275 | let query: Query<_, T> = Query::new(elem); 276 | let node = T::elem_to_node(elem); 277 | self.tree.insert(&query, node) 278 | } 279 | } 280 | 281 | /// Find and remove an element from the tree. 282 | /// 283 | /// If a matching element is found and removed, then `Some(removed_element)` 284 | /// is returned. Otherwise `None` is returned. 285 | /// 286 | /// The `key` must be of a type that implements `TreeOrd` for this tree's 287 | /// `T` type. The element type `T::Elem` must always implement `TreeOrd`, 288 | /// so you can remove an element directly. You can also implement 289 | /// `TreeOrd` for additional key types. This allows you to search the 290 | /// tree without constructing a full element, and remove the element that 291 | /// matches the given key, if any. 292 | #[inline] 293 | pub fn remove(&mut self, key: &K) -> Option<&'a T::Elem> 294 | where 295 | K: ?Sized + TreeOrd<'a, T>, 296 | { 297 | unsafe { 298 | let query: Query<_, T> = Query::new(key); 299 | self.tree.remove(&query).map(|node| T::node_to_elem(node)) 300 | } 301 | } 302 | 303 | /// Pop the root element from the tree. 304 | /// 305 | /// If the tree has a root, it is removed and `Some(root)` is 306 | /// returned. Otherwise, `None` is returned. 307 | #[inline] 308 | pub fn pop_root(&mut self) -> Option<&'a T::Elem> { 309 | unsafe { self.tree.pop_root().map(|node| T::node_to_elem(node)) } 310 | } 311 | 312 | /// Get the minimum element in the tree. 313 | /// 314 | /// If the tree is non-empty, then the minimum element is splayed to the 315 | /// root and `Some(min_elem)` is returned. Otherwise, `None` is returned. 316 | #[inline] 317 | pub fn min(&mut self) -> Option<&'a T::Elem> { 318 | self.tree.min().map(|node| unsafe { T::node_to_elem(node) }) 319 | } 320 | 321 | /// Pop the minimum element from the tree. 322 | /// 323 | /// If the tree is non-empty, then the minimum element is removed and 324 | /// `Some(_)` is returned. Otherwise, `None` is returned. 325 | #[inline] 326 | pub fn pop_min(&mut self) -> Option<&'a T::Elem> { 327 | unsafe { self.tree.pop_min().map(|node| T::node_to_elem(node)) } 328 | } 329 | 330 | /// Get the maximum element in the tree. 331 | /// 332 | /// If the tree is non-empty, then the maximum element is splayed to the 333 | /// root and `Some(max_elem)` is returned. Otherwise, `None` is returned. 334 | #[inline] 335 | pub fn max(&mut self) -> Option<&'a T::Elem> { 336 | self.tree.max().map(|node| unsafe { T::node_to_elem(node) }) 337 | } 338 | 339 | /// Pop the maximum element from the tree. 340 | /// 341 | /// If the tree is non-empty, then the maximum element is removed and 342 | /// `Some(_)` is returned. Otherwise, `None` is returned. 343 | #[inline] 344 | pub fn pop_max(&mut self) -> Option<&'a T::Elem> { 345 | unsafe { self.tree.pop_max().map(|node| T::node_to_elem(node)) } 346 | } 347 | 348 | /// Walk the tree in order. 349 | /// 350 | /// The `C` type controls whether iteration should continue, or break and 351 | /// return a `C::Result` value. You can use `()` as `C`, and that always 352 | /// continues iteration. Using `Result<(), E>` as `C` allows you to halt 353 | /// iteration on error, and propagate the error value. Using `Option` as 354 | /// `C` allows you to search for some value, halt iteration when its found, 355 | /// and return it. 356 | #[inline] 357 | pub fn walk(&self, mut f: F) -> Option 358 | where 359 | F: FnMut(&'a T::Elem) -> C, 360 | C: WalkControl, 361 | { 362 | let mut result = None; 363 | self.tree.walk(&mut |node| unsafe { 364 | let elem = T::node_to_elem(node); 365 | result = f(elem).should_break(); 366 | result.is_none() 367 | }); 368 | result 369 | } 370 | } 371 | 372 | /// A trait that guides whether `SplayTree::walk` should continue or break, and 373 | /// what the return value is. 374 | pub trait WalkControl { 375 | /// The result type that is returned when we break. 376 | type Result; 377 | 378 | /// If iteration should halt, return `Some`. If iteration should continue, 379 | /// return `None`. 380 | fn should_break(self) -> Option; 381 | } 382 | 383 | impl WalkControl for () { 384 | type Result = (); 385 | 386 | fn should_break(self) -> Option<()> { 387 | None 388 | } 389 | } 390 | 391 | impl WalkControl for Option { 392 | type Result = T; 393 | 394 | fn should_break(mut self) -> Option { 395 | self.take() 396 | } 397 | } 398 | 399 | impl WalkControl for Result<(), E> { 400 | type Result = E; 401 | 402 | fn should_break(self) -> Option { 403 | self.err() 404 | } 405 | } 406 | -------------------------------------------------------------------------------- /src/node.rs: -------------------------------------------------------------------------------- 1 | use core::cell::Cell; 2 | use core::fmt; 3 | 4 | /// A splay tree node that is embedded within some container type. 5 | /// 6 | /// The container type may have multiple `Node` members to fit into multiple 7 | /// `SplayTree`s. For example, if you had a set of memory blocks and wanted to 8 | /// query them by either size or alignment. You could have two intrusive 9 | /// `SplayTree`s, one sorted by size and the other by alignment: 10 | /// 11 | /// ``` 12 | /// struct Monster<'a> { 13 | /// // Intrusive node for splay tree sorted by name. 14 | /// by_name: intrusive_splay_tree::Node<'a>, 15 | /// 16 | /// // Intrusive node for splay tree sorted by health. 17 | /// by_health: intrusive_splay_tree::Node<'a>, 18 | /// 19 | /// // The monster's name. 20 | /// name: String, 21 | /// 22 | /// // The monsters health. 23 | /// health: usize, 24 | /// } 25 | /// ``` 26 | pub struct Node<'a> { 27 | pub(crate) left: Cell>>, 28 | pub(crate) right: Cell>>, 29 | } 30 | 31 | impl<'a> Default for Node<'a> { 32 | #[inline] 33 | fn default() -> Node<'a> { 34 | Node { 35 | left: Cell::new(None), 36 | right: Cell::new(None), 37 | } 38 | } 39 | } 40 | 41 | impl<'a> fmt::Debug for Node<'a> { 42 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 43 | f.debug_tuple("Node").finish() 44 | } 45 | } 46 | 47 | impl<'a> Node<'a> { 48 | /// Get this node's left subtree. 49 | /// 50 | /// This is a low-level API, and should only be used for custom tree walking 51 | /// and searching, for example to implement a custom pre-order traversal. 52 | /// 53 | /// Use the unsafe `IntrusiveNode::node_to_elem` method to convert the 54 | /// resulting `Node` reference into a reference to its container element 55 | /// type. 56 | pub fn left(&self) -> Option<&'a Node> { 57 | self.left.get() 58 | } 59 | 60 | /// Get this node's right subtree. 61 | /// 62 | /// This is a low-level API, and should only be used for custom tree walking 63 | /// and searching, for example to implement a custom pre-order traversal. 64 | /// 65 | /// Use the unsafe `IntrusiveNode::node_to_elem` method to convert the 66 | /// resulting `Node` reference into a reference to its container element 67 | /// type. 68 | pub fn right(&self) -> Option<&'a Node> { 69 | self.right.get() 70 | } 71 | 72 | pub(crate) fn walk(&'a self, f: &mut dyn FnMut(&'a Node<'a>) -> bool) -> bool { 73 | if let Some(left) = self.left.get() { 74 | if !left.walk(f) { 75 | return false; 76 | } 77 | } 78 | 79 | if !f(self) { 80 | return false; 81 | } 82 | 83 | if let Some(right) = self.right.get() { 84 | if !right.walk(f) { 85 | return false; 86 | } 87 | } 88 | 89 | true 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tests/all/main.rs: -------------------------------------------------------------------------------- 1 | mod multiple; 2 | mod quickcheck; 3 | mod single; 4 | mod test; 5 | -------------------------------------------------------------------------------- /tests/all/multiple.rs: -------------------------------------------------------------------------------- 1 | use intrusive_splay_tree::{impl_intrusive_node, Node, SplayTree, TreeOrd}; 2 | use std::cmp::{min, Ordering}; 3 | 4 | #[derive(Debug, Default)] 5 | pub struct Multiple<'a> { 6 | by_x: Node<'a>, 7 | by_y: Node<'a>, 8 | pub x: usize, 9 | pub y: usize, 10 | } 11 | 12 | impl<'a> Multiple<'a> { 13 | pub fn new(x: usize, y: usize) -> Multiple<'a> { 14 | Multiple { 15 | x, 16 | y, 17 | ..Default::default() 18 | } 19 | } 20 | } 21 | 22 | pub struct ByX; 23 | 24 | impl_intrusive_node! { 25 | impl<'a> IntrusiveNode<'a> for ByX 26 | where 27 | type Elem = Multiple<'a>, 28 | node = by_x; 29 | } 30 | 31 | impl<'a> TreeOrd<'a, ByX> for Multiple<'a> { 32 | fn tree_cmp(&self, rhs: &Multiple<'a>) -> Ordering { 33 | self.x.cmp(&rhs.x) 34 | } 35 | } 36 | 37 | impl<'a> TreeOrd<'a, ByX> for usize { 38 | fn tree_cmp(&self, rhs: &Multiple<'a>) -> Ordering { 39 | self.cmp(&rhs.x) 40 | } 41 | } 42 | 43 | pub struct ByY; 44 | 45 | intrusive_splay_tree::impl_intrusive_node! { 46 | impl<'a> IntrusiveNode<'a> for ByY 47 | where 48 | type Elem = Multiple<'a>, 49 | node = by_y; 50 | } 51 | 52 | impl<'a> TreeOrd<'a, ByY> for Multiple<'a> { 53 | fn tree_cmp(&self, rhs: &Multiple<'a>) -> Ordering { 54 | self.y.cmp(&rhs.y) 55 | } 56 | } 57 | 58 | impl<'a> TreeOrd<'a, ByY> for usize { 59 | fn tree_cmp(&self, rhs: &Multiple<'a>) -> Ordering { 60 | self.cmp(&rhs.y) 61 | } 62 | } 63 | 64 | pub fn trees_from_xs_and_ys<'a>( 65 | arena: &'a bumpalo::Bump, 66 | xs: Vec, 67 | ys: Vec, 68 | x: usize, 69 | y: usize, 70 | ) -> (SplayTree<'a, ByX>, SplayTree<'a, ByY>, bool, bool) { 71 | let min_len = min(xs.len(), ys.len()); 72 | let mut xs = xs; 73 | let mut ys = ys; 74 | xs.truncate(min_len); 75 | ys.truncate(min_len); 76 | 77 | let x_in_xs = xs.contains(&x); 78 | let y_in_ys = ys.contains(&y); 79 | 80 | let mut by_x = SplayTree::::default(); 81 | let mut by_y = SplayTree::::default(); 82 | for (x, y) in xs.into_iter().zip(ys) { 83 | let m = arena.alloc(Multiple::new(x, y)); 84 | by_x.insert(m); 85 | by_y.insert(m); 86 | } 87 | 88 | (by_x, by_y, x_in_xs, y_in_ys) 89 | } 90 | -------------------------------------------------------------------------------- /tests/all/quickcheck.rs: -------------------------------------------------------------------------------- 1 | use crate::multiple::{trees_from_xs_and_ys, Multiple}; 2 | use crate::single::{Single, SingleTree}; 3 | use intrusive_splay_tree::SplayTree; 4 | use std::iter::FromIterator; 5 | 6 | /// Like the `quickcheck::quickcheck` macro, but limits the number of tests run 7 | /// through MIRI, since MIRI execution is so slow. 8 | #[macro_export] 9 | macro_rules! quickcheck { 10 | ( 11 | $( 12 | $(#[$m:meta])* 13 | fn $fn_name:ident($($arg_name:ident : $arg_ty:ty),*) -> $ret:ty { 14 | $($code:tt)* 15 | } 16 | )* 17 | ) => { 18 | $( 19 | #[test] 20 | $(#[$m])* 21 | fn $fn_name() { 22 | fn prop($($arg_name: $arg_ty),*) -> $ret { 23 | $($code)* 24 | } 25 | 26 | let mut qc = ::quickcheck::QuickCheck::new(); 27 | 28 | // Use the `QUICKCHECK_TESTS` environment variable from 29 | // compiletime to avoid violating MIRI's isolation by looking at 30 | // the runtime environment variable. 31 | let tests = option_env!("QUICKCHECK_TESTS").and_then(|s| s.parse().ok()); 32 | 33 | // Limit quickcheck tests under MIRI, since they are otherwise 34 | // super slow. 35 | #[cfg(miri)] 36 | let tests = tests.or(Some(25)); 37 | 38 | if let Some(tests) = tests { 39 | eprintln!("Executing at most {} quickchecks", tests); 40 | qc = qc.tests(tests); 41 | } 42 | 43 | qc.quickcheck(prop as fn($($arg_ty),*) -> $ret); 44 | } 45 | )* 46 | }; 47 | } 48 | 49 | quickcheck! { 50 | fn find(xs: Vec, x: usize) -> bool { 51 | let x_in_xs = xs.contains(&x); 52 | 53 | let arena = bumpalo::Bump::new(); 54 | 55 | let mut tree = SplayTree::::from_iter( 56 | xs.into_iter() 57 | .map(|x| &*arena.alloc(Single::new(x))) 58 | ); 59 | 60 | if let Some(c) = tree.find(&x) { 61 | x_in_xs && c.value == x 62 | } else { 63 | !x_in_xs 64 | } 65 | } 66 | 67 | fn remove(xs: Vec, x: usize) -> bool { 68 | let x_in_xs = xs.contains(&x); 69 | 70 | let arena = bumpalo::Bump::new(); 71 | 72 | let mut tree = SplayTree::::from_iter( 73 | xs.into_iter() 74 | .map(|x| &*arena.alloc(Single::new(x))) 75 | ); 76 | 77 | if let Some(removed) = tree.remove(&x) { 78 | x_in_xs && removed.value == x && tree.find(&x).is_none() 79 | } else { 80 | !x_in_xs 81 | } 82 | } 83 | 84 | fn insert(xs: Vec, x: usize) -> bool { 85 | let x_in_xs = xs.contains(&x); 86 | 87 | let arena = bumpalo::Bump::new(); 88 | 89 | let mut tree = SplayTree::::from_iter( 90 | xs.into_iter() 91 | .map(|x| &*arena.alloc(Single::new(x))) 92 | ); 93 | 94 | let is_new_entry = tree.insert(arena.alloc(Single::new(x))); 95 | ((is_new_entry && !x_in_xs) || x_in_xs) && tree.find(&x).map_or(false, |c| c.value == x) 96 | } 97 | 98 | fn tree_min(xs: Vec) -> bool { 99 | let min = xs.iter().copied().min(); 100 | 101 | let arena = bumpalo::Bump::new(); 102 | 103 | let mut tree = SplayTree::::from_iter( 104 | xs.into_iter() 105 | .map(|x| &*arena.alloc(Single::new(x))) 106 | ); 107 | 108 | tree.min().map(|s| s.value) == min 109 | } 110 | 111 | fn tree_max(xs: Vec) -> bool { 112 | let max = xs.iter().copied().max(); 113 | 114 | let arena = bumpalo::Bump::new(); 115 | 116 | let mut tree = SplayTree::::from_iter( 117 | xs.into_iter() 118 | .map(|x| &*arena.alloc(Single::new(x))) 119 | ); 120 | 121 | tree.max().map(|s| s.value) == max 122 | } 123 | 124 | fn pop_min(xs: Vec) -> bool { 125 | if xs.is_empty() { 126 | return true; 127 | } 128 | 129 | let arena = bumpalo::Bump::new(); 130 | 131 | let mut tree = SplayTree::::from_iter( 132 | xs.into_iter() 133 | .map(|x| &*arena.alloc(Single::new(x))) 134 | ); 135 | 136 | let mut prev_min = tree.pop_min().unwrap().value; 137 | while let Some(n) = tree.pop_min() { 138 | if n.value < prev_min { 139 | return false; 140 | } 141 | prev_min = n.value; 142 | } 143 | 144 | true 145 | } 146 | 147 | fn pop_max(xs: Vec) -> bool { 148 | if xs.is_empty() { 149 | return true; 150 | } 151 | 152 | let arena = bumpalo::Bump::new(); 153 | 154 | let mut tree = SplayTree::::from_iter( 155 | xs.into_iter() 156 | .map(|x| &*arena.alloc(Single::new(x))) 157 | ); 158 | 159 | let mut prev_max = tree.pop_max().unwrap().value; 160 | while let Some(n) = tree.pop_max() { 161 | if n.value > prev_max { 162 | return false; 163 | } 164 | prev_max = n.value; 165 | } 166 | 167 | true 168 | } 169 | 170 | fn pop_root(xs: Vec) -> bool { 171 | let arena = bumpalo::Bump::new(); 172 | 173 | let mut tree = SplayTree::::from_iter( 174 | xs.into_iter() 175 | .map(|x| &*arena.alloc(Single::new(x))) 176 | ); 177 | 178 | let root = tree.root().map(|n| n.value); 179 | tree.pop_root().map(|n| n.value) == root 180 | } 181 | 182 | fn multiple_find(xs: Vec, ys: Vec, x: usize, y: usize) -> bool { 183 | let arena = bumpalo::Bump::new(); 184 | let (mut by_x, mut by_y, x_in_xs, y_in_ys) = trees_from_xs_and_ys(&arena, xs, ys, x, y); 185 | 186 | let by_x_ok = if let Some(m) = by_x.find(&x) { 187 | x_in_xs && m.x == x 188 | } else { 189 | !x_in_xs 190 | }; 191 | 192 | let by_y_ok = if let Some(m) = by_y.find(&y) { 193 | y_in_ys && m.y == y 194 | } else { 195 | !y_in_ys 196 | }; 197 | 198 | by_x_ok && by_y_ok 199 | } 200 | 201 | fn multiple_remove(xs: Vec, ys: Vec, x: usize, y: usize) -> bool { 202 | let arena = bumpalo::Bump::new(); 203 | let (mut by_x, mut by_y, x_in_xs, y_in_ys) = trees_from_xs_and_ys(&arena, xs, ys, x, y); 204 | 205 | let by_x_ok = if let Some(m) = by_x.remove(&x) { 206 | x_in_xs && m.x == x 207 | } else { 208 | !x_in_xs 209 | }; 210 | let by_x_ok = by_x_ok && by_x.find(&x).is_none(); 211 | 212 | let by_y_ok = if let Some(m) = by_y.remove(&y) { 213 | y_in_ys && m.y == y 214 | } else { 215 | !y_in_ys 216 | }; 217 | let by_y_ok = by_y_ok && by_y.find(&y).is_none(); 218 | 219 | by_x_ok && by_y_ok 220 | } 221 | 222 | fn multiple_insert(xs: Vec, ys: Vec, x: usize, y: usize) -> bool { 223 | let arena = bumpalo::Bump::new(); 224 | let (mut by_x, mut by_y, x_in_xs, y_in_ys) = trees_from_xs_and_ys(&arena, xs, ys, x, y); 225 | 226 | let elem = arena.alloc(Multiple::new(x, y)); 227 | let x_is_new = by_x.insert(elem); 228 | let y_is_new = by_y.insert(elem); 229 | 230 | ((x_is_new && !x_in_xs) || x_in_xs) && by_x.find(&x).map_or(false, |m| m.x == x) && 231 | ((y_is_new && !y_in_ys) || y_in_ys) && by_y.find(&y).map_or(false, |m| m.y == y) 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /tests/all/single.rs: -------------------------------------------------------------------------------- 1 | use intrusive_splay_tree::{IntrusiveNode, Node, TreeOrd}; 2 | use std::cmp::Ordering; 3 | use std::marker::PhantomData; 4 | 5 | #[derive(Debug, Default)] 6 | pub struct Single<'a> { 7 | pub value: usize, 8 | node: Node<'a>, 9 | } 10 | 11 | impl<'a> Single<'a> { 12 | pub fn new(x: usize) -> Single<'a> { 13 | Single { 14 | value: x, 15 | node: Default::default(), 16 | } 17 | } 18 | } 19 | 20 | pub struct SingleTree; 21 | 22 | intrusive_splay_tree::impl_intrusive_node! { 23 | impl<'a> IntrusiveNode<'a> for SingleTree 24 | where 25 | type Elem = Single<'a>, 26 | node = node; 27 | } 28 | 29 | impl<'a> TreeOrd<'a, SingleTree> for Single<'a> { 30 | fn tree_cmp(&self, rhs: &Single<'a>) -> Ordering { 31 | self.value.cmp(&rhs.value) 32 | } 33 | } 34 | 35 | impl<'a> TreeOrd<'a, SingleTree> for usize { 36 | fn tree_cmp(&self, rhs: &Single<'a>) -> Ordering { 37 | self.cmp(&rhs.value) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/all/test.rs: -------------------------------------------------------------------------------- 1 | use crate::single::{Single, SingleTree}; 2 | use intrusive_splay_tree::SplayTree; 3 | use std::panic; 4 | 5 | #[test] 6 | #[cfg(debug_assertions)] 7 | fn inserting_already_inserted_panics_in_debug() { 8 | let result = panic::catch_unwind(panic::AssertUnwindSafe(move || { 9 | let arena = bumpalo::Bump::new(); 10 | let mut tree = SplayTree::::default(); 11 | let elems = (0..3) 12 | .map(|x| arena.alloc(Single::new(x))) 13 | .collect::>(); 14 | 15 | for e in elems.iter() { 16 | tree.insert(e); 17 | } 18 | for e in elems.iter() { 19 | tree.insert(e); 20 | } 21 | })); 22 | assert!(result.is_err()); 23 | } 24 | -------------------------------------------------------------------------------- /tests/multiple.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /wasm/Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "intrusive_splay_tree" 3 | version = "0.1.0" 4 | dependencies = [ 5 | "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 6 | ] 7 | 8 | [[package]] 9 | name = "intrusive_splay_tree_wasm" 10 | version = "0.1.0" 11 | dependencies = [ 12 | "intrusive_splay_tree 0.1.0", 13 | ] 14 | 15 | [[package]] 16 | name = "unreachable" 17 | version = "1.0.0" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | dependencies = [ 20 | "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 21 | ] 22 | 23 | [[package]] 24 | name = "void" 25 | version = "1.0.2" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | 28 | [metadata] 29 | "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" 30 | "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 31 | -------------------------------------------------------------------------------- /wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "intrusive_splay_tree_wasm" 3 | version = "0.1.0" 4 | authors = ["Nick Fitzgerald "] 5 | 6 | [lib] 7 | crate-type = ["cdylib"] 8 | 9 | [dependencies] 10 | intrusive_splay_tree = { path = ".." } 11 | 12 | [profile.release] 13 | opt-level = "s" 14 | lto = true 15 | -------------------------------------------------------------------------------- /wasm/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eux 4 | 5 | cd "$(dirname $0)" 6 | 7 | rustup target add wasm32-unknown-unknown --toolchain nightly 8 | 9 | cargo +nightly build --release --target wasm32-unknown-unknown 10 | 11 | WASM="$(pwd)/target/wasm32-unknown-unknown/release/intrusive_splay_tree_wasm.wasm" 12 | 13 | if [[ -x "$(which wasm-gc)" ]]; then 14 | new_wasm="${WASM/\.wasm/.gc.wasm}" 15 | wasm-gc "$WASM" "$new_wasm" 16 | WASM="$new_wasm" 17 | fi 18 | 19 | if [[ -x "$(which wasm-opt)" ]];then 20 | new_wasm="${WASM/\.wasm/.opt.wasm}" 21 | wasm-opt -Oz "$WASM" -o "$new_wasm" 22 | WASM="$new_wasm" 23 | fi 24 | 25 | cd "$(dirname $WASM)" 26 | ls -1 | grep '\.wasm$' | xargs wc -c 27 | ls -1 | grep '\.wasm$' | xargs -I '{}' sh -c 'echo -n "{} gzipped is "; cat "{}" | gzip --best | wc -c' 28 | -------------------------------------------------------------------------------- /wasm/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(core_intrinsics, lang_items)] 3 | 4 | #[macro_use] 5 | extern crate intrusive_splay_tree; 6 | 7 | use core::cmp::Ordering; 8 | use core::mem; 9 | use core::ptr; 10 | 11 | pub use intrusive_splay_tree::SplayTree; 12 | 13 | // Need to provide a tiny `panic_fmt` lang-item implementation for `#![no_std]`. 14 | // This implementation will translate panics into traps in the resulting 15 | // WebAssembly. 16 | #[lang = "panic_fmt"] 17 | extern "C" fn panic_fmt( 18 | _args: ::core::fmt::Arguments, 19 | _file: &'static str, 20 | _line: u32 21 | ) -> ! { 22 | use core::intrinsics; 23 | unsafe { 24 | intrinsics::abort(); 25 | } 26 | } 27 | 28 | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] 29 | pub struct Id(pub u32); 30 | 31 | #[derive(Debug)] 32 | pub struct Monster<'a> { 33 | id: Id, 34 | health: u32, 35 | by_id_node: intrusive_splay_tree::Node<'a>, 36 | by_health_node: intrusive_splay_tree::Node<'a>, 37 | } 38 | 39 | pub struct MonstersById; 40 | 41 | impl_intrusive_node! { 42 | impl<'a> IntrusiveNode<'a> for MonstersById 43 | where 44 | type Elem = Monster<'a>, 45 | node = by_id_node; 46 | } 47 | 48 | impl<'a> intrusive_splay_tree::TreeOrd<'a, MonstersById> for Monster<'a> { 49 | fn tree_cmp(&self, rhs: &Monster<'a>) -> Ordering { 50 | self.id.cmp(&rhs.id) 51 | } 52 | } 53 | 54 | pub struct MonstersByHealth; 55 | impl_intrusive_node! { 56 | impl<'a> IntrusiveNode<'a> for MonstersByHealth 57 | where 58 | type Elem = Monster<'a>, 59 | node = by_health_node; 60 | } 61 | impl<'a> intrusive_splay_tree::TreeOrd<'a, MonstersByHealth> for Monster<'a> { 62 | fn tree_cmp(&self, rhs: &Monster<'a>) -> Ordering { 63 | self.health.cmp(&rhs.health) 64 | } 65 | } 66 | 67 | impl<'a> intrusive_splay_tree::TreeOrd<'a, MonstersByHealth> for u32 { 68 | fn tree_cmp(&self, rhs: &Monster<'a>) -> Ordering { 69 | self.cmp(&rhs.health) 70 | } 71 | } 72 | 73 | impl<'a> intrusive_splay_tree::TreeOrd<'a, MonstersById> for Id { 74 | fn tree_cmp(&self, rhs: &Monster<'a>) -> Ordering { 75 | self.cmp(&rhs.id) 76 | } 77 | } 78 | 79 | extern "C" { 80 | fn alloc(n: usize) -> *mut u8; 81 | } 82 | 83 | #[no_mangle] 84 | pub unsafe extern "C" fn new_id_tree() -> *mut SplayTree<'static, MonstersById> { 85 | let p = alloc(mem::size_of::>()); 86 | let p = p as *mut SplayTree; 87 | ptr::write(p, SplayTree::default()); 88 | p 89 | } 90 | 91 | #[no_mangle] 92 | pub unsafe extern "C" fn new_health_tree() -> *mut SplayTree<'static, MonstersByHealth> { 93 | let p = alloc(mem::size_of::>()); 94 | let p = p as *mut SplayTree; 95 | ptr::write(p, SplayTree::default()); 96 | p 97 | } 98 | 99 | #[no_mangle] 100 | pub unsafe extern "C" fn new_monster( 101 | id: u32, 102 | health: u32, 103 | by_id_tree: *mut SplayTree<'static, MonstersById>, 104 | by_health_tree: *mut SplayTree<'static, MonstersByHealth>, 105 | ) -> *const Monster<'static> { 106 | let p = alloc(mem::size_of::()); 107 | let p = p as *mut Monster<'static>; 108 | ptr::write( 109 | p, 110 | Monster { 111 | id: Id(id), 112 | health, 113 | by_id_node: Default::default(), 114 | by_health_node: Default::default(), 115 | }, 116 | ); 117 | let monster = &*p; 118 | (*by_id_tree).insert(monster); 119 | (*by_health_tree).insert(monster); 120 | monster 121 | } 122 | 123 | #[no_mangle] 124 | pub unsafe extern "C" fn query_by_id( 125 | tree: *mut SplayTree<'static, MonstersById>, 126 | id: u32, 127 | ) -> *const Monster<'static> { 128 | (*tree).find(&Id(id)).map_or(ptr::null(), |m| m as *const _) 129 | } 130 | 131 | #[no_mangle] 132 | pub unsafe extern "C" fn query_by_health( 133 | tree: *mut SplayTree<'static, MonstersByHealth>, 134 | health: u32, 135 | ) -> *const Monster<'static> { 136 | (*tree).find(&health).map_or(ptr::null(), |m| m as *const _) 137 | } 138 | --------------------------------------------------------------------------------