├── .github └── workflows │ ├── compile-and-test.yml │ ├── fuzz.yml │ └── iai-bench.yml ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── fuzz ├── Cargo.lock ├── Cargo.toml └── src │ └── rart_check.rs └── rart ├── Cargo.lock ├── Cargo.toml ├── benches ├── art_bench.rs ├── art_compare_bench.rs ├── art_iai_bench.rs ├── node_mapping_iai_microbenches.rs └── node_mapping_microbenches.rs └── src ├── concurrent ├── mod.rs └── tree.rs ├── iter.rs ├── keys ├── array_key.rs ├── mod.rs └── vector_key.rs ├── lib.rs ├── mapping ├── direct_mapping.rs ├── indexed_mapping.rs ├── keyed_mapping.rs ├── mod.rs └── sorted_keyed_mapping.rs ├── node.rs ├── partials ├── array_partial.rs ├── mod.rs └── vector_partial.rs ├── range.rs ├── stats.rs ├── tree.rs └── utils ├── bitarray.rs ├── bitset.rs ├── mod.rs ├── optimistic_lock.rs └── u8_keys.rs /.github/workflows/compile-and-test.yml: -------------------------------------------------------------------------------- 1 | name: Build, lint, test, and doc 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | branches: ["main"] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | RUST_BACKTRACE: "1" 12 | 13 | jobs: 14 | base: 15 | name: Build, check and test 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | toolchain: 20 | - stable 21 | - nightly 22 | include: 23 | - toolchain: nightly 24 | components: rustfmt 25 | - toolchain: stable 26 | components: rustfmt, clippy 27 | runs-on: ubuntu-latest 28 | steps: 29 | - uses: actions/checkout@v3 30 | - uses: dtolnay/rust-toolchain@master 31 | with: 32 | toolchain: ${{ matrix.toolchain }} 33 | components: ${{ matrix.components }} 34 | - name: Prepare 35 | if: matrix.toolchain != 'stable' 36 | run: | 37 | rustup toolchain install stable --no-self-update --component clippy 38 | - uses: Swatinem/rust-cache@v2.2.0 39 | with: 40 | key: ubuntu-latest_${{ matrix.toolchain }}" 41 | - name: Build 42 | run: cargo build --all-features 43 | - name: Format 44 | run: cargo fmt -- --check 45 | - name: Lint 46 | run: cargo +stable clippy --all-features --all-targets -- -D warnings 47 | env: 48 | CARGO_REGISTRIES_CRATES_IO_PROTOCOL: git # unstable on 1.66 + 1.67 49 | - name: Test 50 | run: cargo test --all-features 51 | 52 | docs: 53 | needs: [base] 54 | name: Docs/ubuntu-latest 55 | runs-on: ubuntu-latest 56 | steps: 57 | - uses: actions/checkout@v3 58 | - uses: dtolnay/rust-toolchain@stable 59 | - uses: Swatinem/rust-cache@v2.2.0 60 | - name: Run doc tests 61 | run: cargo test --all-features --doc 62 | - name: Check Documentation 63 | run: cargo doc --all-features --no-deps --document-private-items -------------------------------------------------------------------------------- /.github/workflows/fuzz.yml: -------------------------------------------------------------------------------- 1 | name: Run Fuzztest 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | branches: ["main"] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | RUST_BACKTRACE: "1" 12 | 13 | jobs: 14 | base: 15 | name: Fuzztest 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | toolchain: 20 | - nightly 21 | include: 22 | - toolchain: nightly 23 | components: rustfmt 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v3 27 | - uses: dtolnay/rust-toolchain@master 28 | with: 29 | toolchain: ${{ matrix.toolchain }} 30 | components: ${{ matrix.components }} 31 | - uses: Swatinem/rust-cache@v2.2.0 32 | with: 33 | key: ubuntu-latest_${{ matrix.toolchain }}" 34 | - name: Build 35 | run: cargo build --all-features 36 | - name: Install cargo-fuzz 37 | run: cargo install cargo-fuzz 38 | - name: Get fuzzy for 30 seconds 39 | run: cargo fuzz run rart_check -- -max_total_time=30s 40 | -------------------------------------------------------------------------------- /.github/workflows/iai-bench.yml: -------------------------------------------------------------------------------- 1 | name: Run Callgrind Microbenchmark 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | branches: ["main"] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | RUST_BACKTRACE: "1" 12 | 13 | jobs: 14 | base: 15 | name: Build and run microbenchmarks 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | toolchain: 20 | - stable 21 | - nightly 22 | include: 23 | - toolchain: nightly 24 | components: rustfmt 25 | - toolchain: stable 26 | components: rustfmt, clippy 27 | 28 | runs-on: ubuntu-latest 29 | steps: 30 | - uses: actions/checkout@v3 31 | - uses: dtolnay/rust-toolchain@master 32 | with: 33 | toolchain: ${{ matrix.toolchain }} 34 | components: ${{ matrix.components }} 35 | - name: Prepare 36 | if: matrix.toolchain != 'stable' 37 | run: | 38 | rustup toolchain install stable --no-self-update --component clippy 39 | - uses: Swatinem/rust-cache@v2.2.0 40 | with: 41 | key: ubuntu-latest_${{ matrix.toolchain }}" 42 | - name: Install valgrind 43 | run: sudo apt-get -y update && sudo apt-get -y install valgrind 44 | - name: Info 45 | run: | 46 | set -x 47 | uname -a 48 | pwd 49 | rustup --version 50 | rustup show 51 | rustup component list --installed 52 | valgrind --version 53 | - name: Install callgrind runner 54 | run: cargo install --version 0.4.0 iai-callgrind-runner 55 | - name: Bench 56 | run: cargo bench --all-features --bench art_iai_bench --bench node_mapping_iai_microbenches 57 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anes" 7 | version = "0.1.6" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" 10 | 11 | [[package]] 12 | name = "anstyle" 13 | version = "1.0.1" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" 16 | 17 | [[package]] 18 | name = "arbitrary" 19 | version = "1.3.0" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" 22 | dependencies = [ 23 | "derive_arbitrary", 24 | ] 25 | 26 | [[package]] 27 | name = "autocfg" 28 | version = "1.1.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 31 | 32 | [[package]] 33 | name = "bitflags" 34 | version = "1.3.2" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 37 | 38 | [[package]] 39 | name = "bumpalo" 40 | version = "3.13.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" 43 | 44 | [[package]] 45 | name = "cast" 46 | version = "0.3.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 49 | 50 | [[package]] 51 | name = "cc" 52 | version = "1.0.79" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 55 | dependencies = [ 56 | "jobserver", 57 | ] 58 | 59 | [[package]] 60 | name = "cfg-if" 61 | version = "1.0.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 64 | 65 | [[package]] 66 | name = "ciborium" 67 | version = "0.2.1" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" 70 | dependencies = [ 71 | "ciborium-io", 72 | "ciborium-ll", 73 | "serde", 74 | ] 75 | 76 | [[package]] 77 | name = "ciborium-io" 78 | version = "0.2.1" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" 81 | 82 | [[package]] 83 | name = "ciborium-ll" 84 | version = "0.2.1" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" 87 | dependencies = [ 88 | "ciborium-io", 89 | "half", 90 | ] 91 | 92 | [[package]] 93 | name = "clap" 94 | version = "4.3.5" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "2686c4115cb0810d9a984776e197823d08ec94f176549a89a9efded477c456dc" 97 | dependencies = [ 98 | "clap_builder", 99 | ] 100 | 101 | [[package]] 102 | name = "clap_builder" 103 | version = "4.3.5" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "2e53afce1efce6ed1f633cf0e57612fe51db54a1ee4fd8f8503d078fe02d69ae" 106 | dependencies = [ 107 | "anstyle", 108 | "bitflags", 109 | "clap_lex", 110 | ] 111 | 112 | [[package]] 113 | name = "clap_lex" 114 | version = "0.5.0" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" 117 | 118 | [[package]] 119 | name = "criterion" 120 | version = "0.5.1" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" 123 | dependencies = [ 124 | "anes", 125 | "cast", 126 | "ciborium", 127 | "clap", 128 | "criterion-plot", 129 | "is-terminal", 130 | "itertools", 131 | "num-traits", 132 | "once_cell", 133 | "oorandom", 134 | "plotters", 135 | "rayon", 136 | "regex", 137 | "serde", 138 | "serde_derive", 139 | "serde_json", 140 | "tinytemplate", 141 | "walkdir", 142 | ] 143 | 144 | [[package]] 145 | name = "criterion-plot" 146 | version = "0.5.0" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" 149 | dependencies = [ 150 | "cast", 151 | "itertools", 152 | ] 153 | 154 | [[package]] 155 | name = "crossbeam-channel" 156 | version = "0.5.8" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" 159 | dependencies = [ 160 | "cfg-if", 161 | "crossbeam-utils", 162 | ] 163 | 164 | [[package]] 165 | name = "crossbeam-deque" 166 | version = "0.8.3" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" 169 | dependencies = [ 170 | "cfg-if", 171 | "crossbeam-epoch", 172 | "crossbeam-utils", 173 | ] 174 | 175 | [[package]] 176 | name = "crossbeam-epoch" 177 | version = "0.9.15" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" 180 | dependencies = [ 181 | "autocfg", 182 | "cfg-if", 183 | "crossbeam-utils", 184 | "memoffset", 185 | "scopeguard", 186 | ] 187 | 188 | [[package]] 189 | name = "crossbeam-utils" 190 | version = "0.8.16" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" 193 | dependencies = [ 194 | "cfg-if", 195 | ] 196 | 197 | [[package]] 198 | name = "csv" 199 | version = "1.2.2" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086" 202 | dependencies = [ 203 | "csv-core", 204 | "itoa", 205 | "ryu", 206 | "serde", 207 | ] 208 | 209 | [[package]] 210 | name = "csv-core" 211 | version = "0.1.10" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" 214 | dependencies = [ 215 | "memchr", 216 | ] 217 | 218 | [[package]] 219 | name = "derive_arbitrary" 220 | version = "1.3.1" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8" 223 | dependencies = [ 224 | "proc-macro2", 225 | "quote", 226 | "syn", 227 | ] 228 | 229 | [[package]] 230 | name = "either" 231 | version = "1.8.1" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" 234 | 235 | [[package]] 236 | name = "errno" 237 | version = "0.3.1" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" 240 | dependencies = [ 241 | "errno-dragonfly", 242 | "libc", 243 | "windows-sys", 244 | ] 245 | 246 | [[package]] 247 | name = "errno-dragonfly" 248 | version = "0.1.2" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 251 | dependencies = [ 252 | "cc", 253 | "libc", 254 | ] 255 | 256 | [[package]] 257 | name = "getrandom" 258 | version = "0.2.10" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" 261 | dependencies = [ 262 | "cfg-if", 263 | "libc", 264 | "wasi", 265 | ] 266 | 267 | [[package]] 268 | name = "half" 269 | version = "1.8.2" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" 272 | 273 | [[package]] 274 | name = "hermit-abi" 275 | version = "0.2.6" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" 278 | dependencies = [ 279 | "libc", 280 | ] 281 | 282 | [[package]] 283 | name = "hermit-abi" 284 | version = "0.3.1" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" 287 | 288 | [[package]] 289 | name = "iai-callgrind" 290 | version = "0.4.0" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "5ff41e0063f618f5896ad48d6f9cc6c03552f7c2b65abfcf6fbfaa48e8a80c9f" 293 | 294 | [[package]] 295 | name = "io-lifetimes" 296 | version = "1.0.11" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" 299 | dependencies = [ 300 | "hermit-abi 0.3.1", 301 | "libc", 302 | "windows-sys", 303 | ] 304 | 305 | [[package]] 306 | name = "is-terminal" 307 | version = "0.4.7" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" 310 | dependencies = [ 311 | "hermit-abi 0.3.1", 312 | "io-lifetimes", 313 | "rustix", 314 | "windows-sys", 315 | ] 316 | 317 | [[package]] 318 | name = "itertools" 319 | version = "0.10.5" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 322 | dependencies = [ 323 | "either", 324 | ] 325 | 326 | [[package]] 327 | name = "itoa" 328 | version = "1.0.6" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" 331 | 332 | [[package]] 333 | name = "jobserver" 334 | version = "0.1.26" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" 337 | dependencies = [ 338 | "libc", 339 | ] 340 | 341 | [[package]] 342 | name = "js-sys" 343 | version = "0.3.64" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" 346 | dependencies = [ 347 | "wasm-bindgen", 348 | ] 349 | 350 | [[package]] 351 | name = "libc" 352 | version = "0.2.146" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" 355 | 356 | [[package]] 357 | name = "libfuzzer-sys" 358 | version = "0.4.6" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "beb09950ae85a0a94b27676cccf37da5ff13f27076aa1adbc6545dd0d0e1bd4e" 361 | dependencies = [ 362 | "arbitrary", 363 | "cc", 364 | "once_cell", 365 | ] 366 | 367 | [[package]] 368 | name = "linux-raw-sys" 369 | version = "0.3.8" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" 372 | 373 | [[package]] 374 | name = "log" 375 | version = "0.4.19" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" 378 | 379 | [[package]] 380 | name = "memchr" 381 | version = "2.5.0" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 384 | 385 | [[package]] 386 | name = "memoffset" 387 | version = "0.9.0" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" 390 | dependencies = [ 391 | "autocfg", 392 | ] 393 | 394 | [[package]] 395 | name = "num-traits" 396 | version = "0.2.16" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" 399 | dependencies = [ 400 | "autocfg", 401 | ] 402 | 403 | [[package]] 404 | name = "num_cpus" 405 | version = "1.15.0" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" 408 | dependencies = [ 409 | "hermit-abi 0.2.6", 410 | "libc", 411 | ] 412 | 413 | [[package]] 414 | name = "once_cell" 415 | version = "1.18.0" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" 418 | 419 | [[package]] 420 | name = "oorandom" 421 | version = "11.1.3" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" 424 | 425 | [[package]] 426 | name = "paste" 427 | version = "1.0.12" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" 430 | 431 | [[package]] 432 | name = "plotters" 433 | version = "0.3.5" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" 436 | dependencies = [ 437 | "num-traits", 438 | "plotters-backend", 439 | "plotters-svg", 440 | "wasm-bindgen", 441 | "web-sys", 442 | ] 443 | 444 | [[package]] 445 | name = "plotters-backend" 446 | version = "0.3.5" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" 449 | 450 | [[package]] 451 | name = "plotters-svg" 452 | version = "0.3.5" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" 455 | dependencies = [ 456 | "plotters-backend", 457 | ] 458 | 459 | [[package]] 460 | name = "ppv-lite86" 461 | version = "0.2.17" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 464 | 465 | [[package]] 466 | name = "proc-macro2" 467 | version = "1.0.60" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" 470 | dependencies = [ 471 | "unicode-ident", 472 | ] 473 | 474 | [[package]] 475 | name = "quote" 476 | version = "1.0.28" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" 479 | dependencies = [ 480 | "proc-macro2", 481 | ] 482 | 483 | [[package]] 484 | name = "rand" 485 | version = "0.8.5" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 488 | dependencies = [ 489 | "libc", 490 | "rand_chacha", 491 | "rand_core", 492 | ] 493 | 494 | [[package]] 495 | name = "rand_chacha" 496 | version = "0.3.1" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 499 | dependencies = [ 500 | "ppv-lite86", 501 | "rand_core", 502 | ] 503 | 504 | [[package]] 505 | name = "rand_core" 506 | version = "0.6.4" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 509 | dependencies = [ 510 | "getrandom", 511 | ] 512 | 513 | [[package]] 514 | name = "rart" 515 | version = "0.1.0" 516 | dependencies = [ 517 | "criterion", 518 | "crossbeam-utils", 519 | "csv", 520 | "iai-callgrind", 521 | "num-traits", 522 | "paste", 523 | "rand", 524 | "simdeez", 525 | ] 526 | 527 | [[package]] 528 | name = "rart-rs-fuzz" 529 | version = "0.1.0" 530 | dependencies = [ 531 | "arbitrary", 532 | "libfuzzer-sys", 533 | "rart", 534 | ] 535 | 536 | [[package]] 537 | name = "rayon" 538 | version = "1.7.0" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" 541 | dependencies = [ 542 | "either", 543 | "rayon-core", 544 | ] 545 | 546 | [[package]] 547 | name = "rayon-core" 548 | version = "1.11.0" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" 551 | dependencies = [ 552 | "crossbeam-channel", 553 | "crossbeam-deque", 554 | "crossbeam-utils", 555 | "num_cpus", 556 | ] 557 | 558 | [[package]] 559 | name = "regex" 560 | version = "1.8.4" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" 563 | dependencies = [ 564 | "regex-syntax", 565 | ] 566 | 567 | [[package]] 568 | name = "regex-syntax" 569 | version = "0.7.2" 570 | source = "registry+https://github.com/rust-lang/crates.io-index" 571 | checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" 572 | 573 | [[package]] 574 | name = "rustix" 575 | version = "0.37.20" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" 578 | dependencies = [ 579 | "bitflags", 580 | "errno", 581 | "io-lifetimes", 582 | "libc", 583 | "linux-raw-sys", 584 | "windows-sys", 585 | ] 586 | 587 | [[package]] 588 | name = "ryu" 589 | version = "1.0.13" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" 592 | 593 | [[package]] 594 | name = "same-file" 595 | version = "1.0.6" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 598 | dependencies = [ 599 | "winapi-util", 600 | ] 601 | 602 | [[package]] 603 | name = "scopeguard" 604 | version = "1.1.0" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 607 | 608 | [[package]] 609 | name = "serde" 610 | version = "1.0.164" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" 613 | dependencies = [ 614 | "serde_derive", 615 | ] 616 | 617 | [[package]] 618 | name = "serde_derive" 619 | version = "1.0.164" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" 622 | dependencies = [ 623 | "proc-macro2", 624 | "quote", 625 | "syn", 626 | ] 627 | 628 | [[package]] 629 | name = "serde_json" 630 | version = "1.0.97" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a" 633 | dependencies = [ 634 | "itoa", 635 | "ryu", 636 | "serde", 637 | ] 638 | 639 | [[package]] 640 | name = "simdeez" 641 | version = "2.0.0-dev3" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | checksum = "f03e3cf4b84e534adb37350c2ad56fb27e58d10eb2fe8daffc26103b8ae39d3f" 644 | dependencies = [ 645 | "cfg-if", 646 | "paste", 647 | ] 648 | 649 | [[package]] 650 | name = "syn" 651 | version = "2.0.18" 652 | source = "registry+https://github.com/rust-lang/crates.io-index" 653 | checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" 654 | dependencies = [ 655 | "proc-macro2", 656 | "quote", 657 | "unicode-ident", 658 | ] 659 | 660 | [[package]] 661 | name = "tinytemplate" 662 | version = "1.2.1" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" 665 | dependencies = [ 666 | "serde", 667 | "serde_json", 668 | ] 669 | 670 | [[package]] 671 | name = "unicode-ident" 672 | version = "1.0.9" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" 675 | 676 | [[package]] 677 | name = "walkdir" 678 | version = "2.3.3" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" 681 | dependencies = [ 682 | "same-file", 683 | "winapi-util", 684 | ] 685 | 686 | [[package]] 687 | name = "wasi" 688 | version = "0.11.0+wasi-snapshot-preview1" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 691 | 692 | [[package]] 693 | name = "wasm-bindgen" 694 | version = "0.2.87" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" 697 | dependencies = [ 698 | "cfg-if", 699 | "wasm-bindgen-macro", 700 | ] 701 | 702 | [[package]] 703 | name = "wasm-bindgen-backend" 704 | version = "0.2.87" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" 707 | dependencies = [ 708 | "bumpalo", 709 | "log", 710 | "once_cell", 711 | "proc-macro2", 712 | "quote", 713 | "syn", 714 | "wasm-bindgen-shared", 715 | ] 716 | 717 | [[package]] 718 | name = "wasm-bindgen-macro" 719 | version = "0.2.87" 720 | source = "registry+https://github.com/rust-lang/crates.io-index" 721 | checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" 722 | dependencies = [ 723 | "quote", 724 | "wasm-bindgen-macro-support", 725 | ] 726 | 727 | [[package]] 728 | name = "wasm-bindgen-macro-support" 729 | version = "0.2.87" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" 732 | dependencies = [ 733 | "proc-macro2", 734 | "quote", 735 | "syn", 736 | "wasm-bindgen-backend", 737 | "wasm-bindgen-shared", 738 | ] 739 | 740 | [[package]] 741 | name = "wasm-bindgen-shared" 742 | version = "0.2.87" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" 745 | 746 | [[package]] 747 | name = "web-sys" 748 | version = "0.3.64" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" 751 | dependencies = [ 752 | "js-sys", 753 | "wasm-bindgen", 754 | ] 755 | 756 | [[package]] 757 | name = "winapi" 758 | version = "0.3.9" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 761 | dependencies = [ 762 | "winapi-i686-pc-windows-gnu", 763 | "winapi-x86_64-pc-windows-gnu", 764 | ] 765 | 766 | [[package]] 767 | name = "winapi-i686-pc-windows-gnu" 768 | version = "0.4.0" 769 | source = "registry+https://github.com/rust-lang/crates.io-index" 770 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 771 | 772 | [[package]] 773 | name = "winapi-util" 774 | version = "0.1.5" 775 | source = "registry+https://github.com/rust-lang/crates.io-index" 776 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 777 | dependencies = [ 778 | "winapi", 779 | ] 780 | 781 | [[package]] 782 | name = "winapi-x86_64-pc-windows-gnu" 783 | version = "0.4.0" 784 | source = "registry+https://github.com/rust-lang/crates.io-index" 785 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 786 | 787 | [[package]] 788 | name = "windows-sys" 789 | version = "0.48.0" 790 | source = "registry+https://github.com/rust-lang/crates.io-index" 791 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 792 | dependencies = [ 793 | "windows-targets", 794 | ] 795 | 796 | [[package]] 797 | name = "windows-targets" 798 | version = "0.48.0" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" 801 | dependencies = [ 802 | "windows_aarch64_gnullvm", 803 | "windows_aarch64_msvc", 804 | "windows_i686_gnu", 805 | "windows_i686_msvc", 806 | "windows_x86_64_gnu", 807 | "windows_x86_64_gnullvm", 808 | "windows_x86_64_msvc", 809 | ] 810 | 811 | [[package]] 812 | name = "windows_aarch64_gnullvm" 813 | version = "0.48.0" 814 | source = "registry+https://github.com/rust-lang/crates.io-index" 815 | checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" 816 | 817 | [[package]] 818 | name = "windows_aarch64_msvc" 819 | version = "0.48.0" 820 | source = "registry+https://github.com/rust-lang/crates.io-index" 821 | checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" 822 | 823 | [[package]] 824 | name = "windows_i686_gnu" 825 | version = "0.48.0" 826 | source = "registry+https://github.com/rust-lang/crates.io-index" 827 | checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" 828 | 829 | [[package]] 830 | name = "windows_i686_msvc" 831 | version = "0.48.0" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" 834 | 835 | [[package]] 836 | name = "windows_x86_64_gnu" 837 | version = "0.48.0" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" 840 | 841 | [[package]] 842 | name = "windows_x86_64_gnullvm" 843 | version = "0.48.0" 844 | source = "registry+https://github.com/rust-lang/crates.io-index" 845 | checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" 846 | 847 | [[package]] 848 | name = "windows_x86_64_msvc" 849 | version = "0.48.0" 850 | source = "registry+https://github.com/rust-lang/crates.io-index" 851 | checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" 852 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "fuzz", 6 | "rart" 7 | ] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ryan's Adaptive Radix Tree 2 | 3 | This is yet another implementation of an Adaptive Radix Tree (ART) in Rust. ARTs are an ordered associative (key-value) structure that outperform BTrees for many use cases. 4 | 5 | The implementation is based on the paper [The Adaptive Radix Tree: ARTful Indexing for Main-Memory Databases](https://db.in.tum.de/~leis/papers/ART.pdf) by Viktor Leis, Alfons Kemper, and Thomas Neumann. 6 | 7 | I have not (yet) implemented the concurrent form described in later papers. 8 | 9 | During implementation I looked at the following implementations, and borrowed some flow and ideas from them: 10 | * https://github.com/armon/libart/ A C implementation 11 | * https://github.com/rafaelkallis/adaptive-radix-tree C++ implementation 12 | * https://github.com/Lagrang/art-rs Another implementation in Rust. (The criterion benches I have were on the whole borrowed from here originally) 13 | 14 | Performance boost can be gained through fixed sized keys and partials. There are a selection of key types and partials provided under the `partials` module. 15 | 16 | I believe my implementation to be competitive, performance wise. The included benches compare against `BTreeMap`, 17 | `HashMap`, and `art-rs` and show what you'd expect: performance between a hashtable and a btree for random inserts and 18 | retrievals, but sequential inserts and retrievals are much faster than either. 19 | 20 | Some notes: 21 | 22 | * Minimal external dependencies. 23 | * Compiles for `stable` Rust. 24 | * There are some bits of compartmentalized `unsafe` code for performance reasons, but the public API is safe. 25 | * Uses explicit SIMD optimizations for x86 SSE for the 16-child inner keyed node; an implementation for ARM NEON is also there, but doesn't really provide 26 | much performance benefit. 27 | * A fuzz test (under `fuzz/`) is included, but will fully work for `nightly` only. Has already been used to identify 28 | some bugs 29 | * So Much Depends On The Choice of Key & Partial Implementation. Fixed size stack allocated keys and partials 30 | outperform dynamic sized keys and partials in benchmarks. So generally, ArrayKey/ArrayPartial is the way to go. 31 | * The ergonomics of key creation is not great, but I'm not sure how to improve it. Suggestions welcome. 32 | * Room for optimization in range query optimizations, where plenty of unnecessary copy operations are performed. 33 | 34 | More documentation to come. Still working at smoothing the rough corners. Contributions welcome. 35 | -------------------------------------------------------------------------------- /fuzz/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "arbitrary" 7 | version = "1.3.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" 10 | dependencies = [ 11 | "derive_arbitrary", 12 | ] 13 | 14 | [[package]] 15 | name = "autocfg" 16 | version = "1.1.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 19 | 20 | [[package]] 21 | name = "cc" 22 | version = "1.0.79" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 25 | dependencies = [ 26 | "jobserver", 27 | ] 28 | 29 | [[package]] 30 | name = "derive_arbitrary" 31 | version = "1.3.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8" 34 | dependencies = [ 35 | "proc-macro2", 36 | "quote", 37 | "syn", 38 | ] 39 | 40 | [[package]] 41 | name = "jobserver" 42 | version = "0.1.26" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" 45 | dependencies = [ 46 | "libc", 47 | ] 48 | 49 | [[package]] 50 | name = "libc" 51 | version = "0.2.146" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" 54 | 55 | [[package]] 56 | name = "libfuzzer-sys" 57 | version = "0.4.6" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "beb09950ae85a0a94b27676cccf37da5ff13f27076aa1adbc6545dd0d0e1bd4e" 60 | dependencies = [ 61 | "arbitrary", 62 | "cc", 63 | "once_cell", 64 | ] 65 | 66 | [[package]] 67 | name = "num-traits" 68 | version = "0.2.15" 69 | source = "git+https://github.com/rust-num/num-traits.git?rev=c696ef9e8b51a15fb47dbc9a0e1c783f9c615d8a#c696ef9e8b51a15fb47dbc9a0e1c783f9c615d8a" 70 | dependencies = [ 71 | "autocfg", 72 | ] 73 | 74 | [[package]] 75 | name = "once_cell" 76 | version = "1.18.0" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" 79 | 80 | [[package]] 81 | name = "proc-macro2" 82 | version = "1.0.60" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" 85 | dependencies = [ 86 | "unicode-ident", 87 | ] 88 | 89 | [[package]] 90 | name = "quote" 91 | version = "1.0.28" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" 94 | dependencies = [ 95 | "proc-macro2", 96 | ] 97 | 98 | [[package]] 99 | name = "rart" 100 | version = "0.1.0" 101 | dependencies = [ 102 | "num-traits", 103 | ] 104 | 105 | [[package]] 106 | name = "rart-rs-fuzz" 107 | version = "0.1.0" 108 | dependencies = [ 109 | "arbitrary", 110 | "libfuzzer-sys", 111 | "rart", 112 | ] 113 | 114 | [[package]] 115 | name = "syn" 116 | version = "2.0.18" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" 119 | dependencies = [ 120 | "proc-macro2", 121 | "quote", 122 | "unicode-ident", 123 | ] 124 | 125 | [[package]] 126 | name = "unicode-ident" 127 | version = "1.0.9" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" 130 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rart-rs-fuzz" 3 | version = "0.1.0" 4 | authors = ["Automatically generated"] 5 | publish = false 6 | edition = "2021" 7 | 8 | [package.metadata] 9 | cargo-fuzz = true 10 | 11 | [dependencies] 12 | libfuzzer-sys = "0.4.6" 13 | arbitrary = { version = "1.3.0", features = ["derive"] } 14 | 15 | [dependencies.rart] 16 | path = "../rart" 17 | 18 | [[bin]] 19 | name = "rart_check" 20 | path = "src/rart_check.rs" 21 | test = false 22 | doc = false -------------------------------------------------------------------------------- /fuzz/src/rart_check.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use std::collections::HashMap; 4 | 5 | use arbitrary::Arbitrary; 6 | use libfuzzer_sys::fuzz_target; 7 | 8 | use rart::keys::array_key::ArrayKey; 9 | 10 | use rart::tree::AdaptiveRadixTree; 11 | use rart::TreeTrait; 12 | 13 | #[derive(Arbitrary, Debug)] 14 | enum MapMethod { 15 | Get { key: usize }, 16 | Insert { key: usize, val: usize }, 17 | Update { key: usize, val: usize }, 18 | Delete { key: usize }, 19 | } 20 | 21 | fuzz_target!(|methods: Vec| { 22 | let mut art = AdaptiveRadixTree::, usize>::new(); 23 | let mut bt_map = HashMap::::new(); 24 | 25 | for m in methods { 26 | match m { 27 | MapMethod::Get { key } => { 28 | let art_v = art.get(key).copied(); 29 | let bt_v = bt_map.get(&key).copied(); 30 | assert_eq!(art_v, bt_v); 31 | } 32 | MapMethod::Insert { key, val } => { 33 | let btree_insert = bt_map.insert(key, val); 34 | let a_insert = art.insert(key, val); 35 | assert_eq!(a_insert, btree_insert); 36 | } 37 | MapMethod::Update { key, val } => { 38 | let old_bt = bt_map.get_mut(&key); 39 | let old_art = art.get_mut(key); 40 | assert_eq!(old_art, old_bt); 41 | 42 | if let Some(old_bt) = old_bt { 43 | *old_bt = val; 44 | *old_art.unwrap() = val; 45 | 46 | let new_bt = bt_map.get(&key); 47 | let new_art = art.get(key); 48 | assert_eq!(new_art, new_bt); 49 | } 50 | } 51 | MapMethod::Delete { key } => { 52 | let bt_result = bt_map.remove(&key); 53 | let art_result = art.remove(key); 54 | assert_eq!(bt_result, art_result); 55 | } 56 | } 57 | } 58 | 59 | for (k, expected_value) in bt_map.iter() { 60 | let result = art.get(k); 61 | assert_eq!( 62 | result, 63 | Some(expected_value), 64 | "Expected value for key {}: {:?} != {:?}, got {:?}", 65 | k, 66 | art.get(k).copied(), 67 | *expected_value, 68 | result 69 | ); 70 | } 71 | }); 72 | -------------------------------------------------------------------------------- /rart/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anes" 7 | version = "0.1.6" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" 10 | 11 | [[package]] 12 | name = "atty" 13 | version = "0.2.14" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 16 | dependencies = [ 17 | "hermit-abi 0.1.19", 18 | "libc", 19 | "winapi", 20 | ] 21 | 22 | [[package]] 23 | name = "autocfg" 24 | version = "1.1.0" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 27 | 28 | [[package]] 29 | name = "bitflags" 30 | version = "1.3.2" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 33 | 34 | [[package]] 35 | name = "bumpalo" 36 | version = "3.12.1" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" 39 | 40 | [[package]] 41 | name = "cast" 42 | version = "0.3.0" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 45 | 46 | [[package]] 47 | name = "cfg-if" 48 | version = "1.0.0" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 51 | 52 | [[package]] 53 | name = "ciborium" 54 | version = "0.2.0" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" 57 | dependencies = [ 58 | "ciborium-io", 59 | "ciborium-ll", 60 | "serde", 61 | ] 62 | 63 | [[package]] 64 | name = "ciborium-io" 65 | version = "0.2.0" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" 68 | 69 | [[package]] 70 | name = "ciborium-ll" 71 | version = "0.2.0" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" 74 | dependencies = [ 75 | "ciborium-io", 76 | "half", 77 | ] 78 | 79 | [[package]] 80 | name = "clap" 81 | version = "3.2.24" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "eef2b3ded6a26dfaec672a742c93c8cf6b689220324da509ec5caa20de55dc83" 84 | dependencies = [ 85 | "bitflags", 86 | "clap_lex", 87 | "indexmap", 88 | "textwrap", 89 | ] 90 | 91 | [[package]] 92 | name = "clap_lex" 93 | version = "0.2.4" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" 96 | dependencies = [ 97 | "os_str_bytes", 98 | ] 99 | 100 | [[package]] 101 | name = "criterion" 102 | version = "0.4.0" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" 105 | dependencies = [ 106 | "anes", 107 | "atty", 108 | "cast", 109 | "ciborium", 110 | "clap", 111 | "criterion-plot", 112 | "itertools", 113 | "lazy_static", 114 | "num-traits 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", 115 | "oorandom", 116 | "plotters", 117 | "rayon", 118 | "regex", 119 | "serde", 120 | "serde_derive", 121 | "serde_json", 122 | "tinytemplate", 123 | "walkdir", 124 | ] 125 | 126 | [[package]] 127 | name = "criterion-plot" 128 | version = "0.5.0" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" 131 | dependencies = [ 132 | "cast", 133 | "itertools", 134 | ] 135 | 136 | [[package]] 137 | name = "crossbeam-channel" 138 | version = "0.5.8" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" 141 | dependencies = [ 142 | "cfg-if", 143 | "crossbeam-utils", 144 | ] 145 | 146 | [[package]] 147 | name = "crossbeam-deque" 148 | version = "0.8.3" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" 151 | dependencies = [ 152 | "cfg-if", 153 | "crossbeam-epoch", 154 | "crossbeam-utils", 155 | ] 156 | 157 | [[package]] 158 | name = "crossbeam-epoch" 159 | version = "0.9.14" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" 162 | dependencies = [ 163 | "autocfg", 164 | "cfg-if", 165 | "crossbeam-utils", 166 | "memoffset", 167 | "scopeguard", 168 | ] 169 | 170 | [[package]] 171 | name = "crossbeam-utils" 172 | version = "0.8.15" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" 175 | dependencies = [ 176 | "cfg-if", 177 | ] 178 | 179 | [[package]] 180 | name = "either" 181 | version = "1.8.1" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" 184 | 185 | [[package]] 186 | name = "getrandom" 187 | version = "0.2.9" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" 190 | dependencies = [ 191 | "cfg-if", 192 | "libc", 193 | "wasi", 194 | ] 195 | 196 | [[package]] 197 | name = "half" 198 | version = "1.8.2" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" 201 | 202 | [[package]] 203 | name = "hashbrown" 204 | version = "0.12.3" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 207 | 208 | [[package]] 209 | name = "hermit-abi" 210 | version = "0.1.19" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 213 | dependencies = [ 214 | "libc", 215 | ] 216 | 217 | [[package]] 218 | name = "hermit-abi" 219 | version = "0.2.6" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" 222 | dependencies = [ 223 | "libc", 224 | ] 225 | 226 | [[package]] 227 | name = "indexmap" 228 | version = "1.9.3" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 231 | dependencies = [ 232 | "autocfg", 233 | "hashbrown", 234 | ] 235 | 236 | [[package]] 237 | name = "itertools" 238 | version = "0.10.5" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 241 | dependencies = [ 242 | "either", 243 | ] 244 | 245 | [[package]] 246 | name = "itoa" 247 | version = "1.0.6" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" 250 | 251 | [[package]] 252 | name = "js-sys" 253 | version = "0.3.61" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" 256 | dependencies = [ 257 | "wasm-bindgen", 258 | ] 259 | 260 | [[package]] 261 | name = "lazy_static" 262 | version = "1.4.0" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 265 | 266 | [[package]] 267 | name = "libc" 268 | version = "0.2.142" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" 271 | 272 | [[package]] 273 | name = "log" 274 | version = "0.4.17" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 277 | dependencies = [ 278 | "cfg-if", 279 | ] 280 | 281 | [[package]] 282 | name = "memoffset" 283 | version = "0.8.0" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" 286 | dependencies = [ 287 | "autocfg", 288 | ] 289 | 290 | [[package]] 291 | name = "num-traits" 292 | version = "0.2.15" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 295 | dependencies = [ 296 | "autocfg", 297 | ] 298 | 299 | [[package]] 300 | name = "num-traits" 301 | version = "0.2.15" 302 | source = "git+https://github.com/rust-num/num-traits.git?rev=c696ef9e8b51a15fb47dbc9a0e1c783f9c615d8a#c696ef9e8b51a15fb47dbc9a0e1c783f9c615d8a" 303 | dependencies = [ 304 | "autocfg", 305 | ] 306 | 307 | [[package]] 308 | name = "num_cpus" 309 | version = "1.15.0" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" 312 | dependencies = [ 313 | "hermit-abi 0.2.6", 314 | "libc", 315 | ] 316 | 317 | [[package]] 318 | name = "once_cell" 319 | version = "1.17.1" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" 322 | 323 | [[package]] 324 | name = "oorandom" 325 | version = "11.1.3" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" 328 | 329 | [[package]] 330 | name = "os_str_bytes" 331 | version = "6.5.0" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" 334 | 335 | [[package]] 336 | name = "plotters" 337 | version = "0.3.4" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" 340 | dependencies = [ 341 | "num-traits 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", 342 | "plotters-backend", 343 | "plotters-svg", 344 | "wasm-bindgen", 345 | "web-sys", 346 | ] 347 | 348 | [[package]] 349 | name = "plotters-backend" 350 | version = "0.3.4" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" 353 | 354 | [[package]] 355 | name = "plotters-svg" 356 | version = "0.3.3" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" 359 | dependencies = [ 360 | "plotters-backend", 361 | ] 362 | 363 | [[package]] 364 | name = "ppv-lite86" 365 | version = "0.2.17" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 368 | 369 | [[package]] 370 | name = "proc-macro2" 371 | version = "1.0.56" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" 374 | dependencies = [ 375 | "unicode-ident", 376 | ] 377 | 378 | [[package]] 379 | name = "quote" 380 | version = "1.0.26" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" 383 | dependencies = [ 384 | "proc-macro2", 385 | ] 386 | 387 | [[package]] 388 | name = "rand" 389 | version = "0.8.5" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 392 | dependencies = [ 393 | "libc", 394 | "rand_chacha", 395 | "rand_core", 396 | ] 397 | 398 | [[package]] 399 | name = "rand_chacha" 400 | version = "0.3.1" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 403 | dependencies = [ 404 | "ppv-lite86", 405 | "rand_core", 406 | ] 407 | 408 | [[package]] 409 | name = "rand_core" 410 | version = "0.6.4" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 413 | dependencies = [ 414 | "getrandom", 415 | ] 416 | 417 | [[package]] 418 | name = "rart" 419 | version = "0.1.0" 420 | dependencies = [ 421 | "criterion", 422 | "num-traits 0.2.15 (git+https://github.com/rust-num/num-traits.git?rev=c696ef9e8b51a15fb47dbc9a0e1c783f9c615d8a)", 423 | "rand", 424 | ] 425 | 426 | [[package]] 427 | name = "rayon" 428 | version = "1.7.0" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" 431 | dependencies = [ 432 | "either", 433 | "rayon-core", 434 | ] 435 | 436 | [[package]] 437 | name = "rayon-core" 438 | version = "1.11.0" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" 441 | dependencies = [ 442 | "crossbeam-channel", 443 | "crossbeam-deque", 444 | "crossbeam-utils", 445 | "num_cpus", 446 | ] 447 | 448 | [[package]] 449 | name = "regex" 450 | version = "1.8.1" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" 453 | dependencies = [ 454 | "regex-syntax", 455 | ] 456 | 457 | [[package]] 458 | name = "regex-syntax" 459 | version = "0.7.1" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" 462 | 463 | [[package]] 464 | name = "ryu" 465 | version = "1.0.13" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" 468 | 469 | [[package]] 470 | name = "same-file" 471 | version = "1.0.6" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 474 | dependencies = [ 475 | "winapi-util", 476 | ] 477 | 478 | [[package]] 479 | name = "scopeguard" 480 | version = "1.1.0" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 483 | 484 | [[package]] 485 | name = "serde" 486 | version = "1.0.160" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" 489 | dependencies = [ 490 | "serde_derive", 491 | ] 492 | 493 | [[package]] 494 | name = "serde_derive" 495 | version = "1.0.160" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" 498 | dependencies = [ 499 | "proc-macro2", 500 | "quote", 501 | "syn 2.0.15", 502 | ] 503 | 504 | [[package]] 505 | name = "serde_json" 506 | version = "1.0.96" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" 509 | dependencies = [ 510 | "itoa", 511 | "ryu", 512 | "serde", 513 | ] 514 | 515 | [[package]] 516 | name = "syn" 517 | version = "1.0.109" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 520 | dependencies = [ 521 | "proc-macro2", 522 | "quote", 523 | "unicode-ident", 524 | ] 525 | 526 | [[package]] 527 | name = "syn" 528 | version = "2.0.15" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" 531 | dependencies = [ 532 | "proc-macro2", 533 | "quote", 534 | "unicode-ident", 535 | ] 536 | 537 | [[package]] 538 | name = "textwrap" 539 | version = "0.16.0" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" 542 | 543 | [[package]] 544 | name = "tinytemplate" 545 | version = "1.2.1" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" 548 | dependencies = [ 549 | "serde", 550 | "serde_json", 551 | ] 552 | 553 | [[package]] 554 | name = "unicode-ident" 555 | version = "1.0.8" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 558 | 559 | [[package]] 560 | name = "walkdir" 561 | version = "2.3.3" 562 | source = "registry+https://github.com/rust-lang/crates.io-index" 563 | checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" 564 | dependencies = [ 565 | "same-file", 566 | "winapi-util", 567 | ] 568 | 569 | [[package]] 570 | name = "wasi" 571 | version = "0.11.0+wasi-snapshot-preview1" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 574 | 575 | [[package]] 576 | name = "wasm-bindgen" 577 | version = "0.2.84" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" 580 | dependencies = [ 581 | "cfg-if", 582 | "wasm-bindgen-macro", 583 | ] 584 | 585 | [[package]] 586 | name = "wasm-bindgen-backend" 587 | version = "0.2.84" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" 590 | dependencies = [ 591 | "bumpalo", 592 | "log", 593 | "once_cell", 594 | "proc-macro2", 595 | "quote", 596 | "syn 1.0.109", 597 | "wasm-bindgen-shared", 598 | ] 599 | 600 | [[package]] 601 | name = "wasm-bindgen-macro" 602 | version = "0.2.84" 603 | source = "registry+https://github.com/rust-lang/crates.io-index" 604 | checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" 605 | dependencies = [ 606 | "quote", 607 | "wasm-bindgen-macro-support", 608 | ] 609 | 610 | [[package]] 611 | name = "wasm-bindgen-macro-support" 612 | version = "0.2.84" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" 615 | dependencies = [ 616 | "proc-macro2", 617 | "quote", 618 | "syn 1.0.109", 619 | "wasm-bindgen-backend", 620 | "wasm-bindgen-shared", 621 | ] 622 | 623 | [[package]] 624 | name = "wasm-bindgen-shared" 625 | version = "0.2.84" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" 628 | 629 | [[package]] 630 | name = "web-sys" 631 | version = "0.3.61" 632 | source = "registry+https://github.com/rust-lang/crates.io-index" 633 | checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" 634 | dependencies = [ 635 | "js-sys", 636 | "wasm-bindgen", 637 | ] 638 | 639 | [[package]] 640 | name = "winapi" 641 | version = "0.3.9" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 644 | dependencies = [ 645 | "winapi-i686-pc-windows-gnu", 646 | "winapi-x86_64-pc-windows-gnu", 647 | ] 648 | 649 | [[package]] 650 | name = "winapi-i686-pc-windows-gnu" 651 | version = "0.4.0" 652 | source = "registry+https://github.com/rust-lang/crates.io-index" 653 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 654 | 655 | [[package]] 656 | name = "winapi-util" 657 | version = "0.1.5" 658 | source = "registry+https://github.com/rust-lang/crates.io-index" 659 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 660 | dependencies = [ 661 | "winapi", 662 | ] 663 | 664 | [[package]] 665 | name = "winapi-x86_64-pc-windows-gnu" 666 | version = "0.4.0" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 669 | -------------------------------------------------------------------------------- /rart/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rart" 3 | description = "Ryan's Adaptive Radix Tree" 4 | authors = ["Ryan Daum "] 5 | license = "Apache-2.0" 6 | homepage = "https://github.com/rdaum/rart-rs" 7 | repository = "https://github.com/rdaum/rart-rs" 8 | keywords = ["datastructure", "tree", "radix", "index"] 9 | version = "0.1.0" 10 | edition = "2021" 11 | 12 | [dev-dependencies] 13 | criterion = { version = "0.5.1", features = ["html_reports"] } 14 | iai-callgrind = "0.4.0" 15 | paste = "1.0.12" 16 | rand = "0.8" 17 | csv="1.2.1" 18 | 19 | [features] 20 | default = ["simd_keys"] 21 | simd_keys = ["dep:simdeez"] 22 | 23 | [dependencies] 24 | num-traits = "0.2.16" 25 | simdeez = { version = "2.0.0-dev3", optional = true } 26 | crossbeam-utils = "0.8.16" 27 | 28 | [[bench]] 29 | name = "art_bench" 30 | harness = false 31 | 32 | [[bench]] 33 | name = "art_compare_bench" 34 | harness = false 35 | 36 | [[bench]] 37 | name = "node_mapping_microbenches" 38 | harness = false 39 | 40 | [[bench]] 41 | name = "art_iai_bench" 42 | harness = false 43 | 44 | [[bench]] 45 | name = "node_mapping_iai_microbenches" 46 | harness = false 47 | -------------------------------------------------------------------------------- /rart/benches/art_bench.rs: -------------------------------------------------------------------------------- 1 | /// Overall simple performance bench for static # of keys in a few secnarios. Here to quickly test\ 2 | /// for regressions. 3 | use std::time::{Duration, Instant}; 4 | 5 | use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; 6 | use rand::prelude::SliceRandom; 7 | use rand::{thread_rng, Rng}; 8 | 9 | use rart::keys::array_key::ArrayKey; 10 | 11 | use rart::tree::AdaptiveRadixTree; 12 | use rart::TreeTrait; 13 | 14 | // Variations on the number of keys to insert into the tree for benchmarks that measure retrievals 15 | const TREE_SIZES: [u64; 4] = [1 << 15, 1 << 20, 1 << 22, 1 << 24]; 16 | 17 | pub fn rand_insert(c: &mut Criterion) { 18 | let mut group = c.benchmark_group("rand_insert"); 19 | group.throughput(Throughput::Elements(1)); 20 | 21 | let keys = gen_keys(3, 2, 3); 22 | let cached_keys = gen_cached_keys(3, 2, 3); 23 | 24 | group.bench_function("cached_keys", |b| { 25 | let mut tree = AdaptiveRadixTree::, _>::new(); 26 | let mut rng = thread_rng(); 27 | b.iter(|| { 28 | let key = &cached_keys[rng.gen_range(0..cached_keys.len())]; 29 | tree.insert_k(&key.0, key.1.clone()); 30 | }) 31 | }); 32 | 33 | group.bench_function("uncached_keys", |b| { 34 | let mut tree = AdaptiveRadixTree::, _>::new(); 35 | let mut rng = thread_rng(); 36 | b.iter(|| { 37 | let key = &keys[rng.gen_range(0..keys.len())]; 38 | tree.insert(key, key.clone()); 39 | }) 40 | }); 41 | 42 | group.finish(); 43 | } 44 | 45 | pub fn rand_remove(c: &mut Criterion) { 46 | let mut group = c.benchmark_group("rand_remove"); 47 | let keys = gen_keys(3, 2, 3); 48 | let cached_keys = gen_cached_keys(3, 2, 3); 49 | 50 | group.throughput(Throughput::Elements(1)); 51 | group.bench_function("cached_keys", |b| { 52 | let mut tree = AdaptiveRadixTree::, _>::new(); 53 | let mut rng = thread_rng(); 54 | for key in &cached_keys { 55 | tree.insert_k(&key.0.clone(), key.1.clone()); 56 | } 57 | b.iter(|| { 58 | let key = &cached_keys[rng.gen_range(0..keys.len())]; 59 | criterion::black_box(tree.remove_k(&key.0)); 60 | }) 61 | }); 62 | group.bench_function("uncached_keys", |b| { 63 | let mut tree = AdaptiveRadixTree::, _>::new(); 64 | let mut rng = thread_rng(); 65 | for key in &keys { 66 | tree.insert(key, key); 67 | } 68 | b.iter(|| { 69 | let key = &keys[rng.gen_range(0..keys.len())]; 70 | criterion::black_box(tree.remove(key)); 71 | }) 72 | }); 73 | 74 | group.finish(); 75 | } 76 | 77 | pub fn rand_get(c: &mut Criterion) { 78 | for size in TREE_SIZES { 79 | c.bench_with_input(BenchmarkId::new("rand_get", size), &size, |b, size| { 80 | let mut tree = AdaptiveRadixTree::, _>::new(); 81 | for i in 0..*size { 82 | tree.insert(i, i); 83 | } 84 | let mut rng = thread_rng(); 85 | b.iter(|| { 86 | let key = rng.gen_range(0..*size); 87 | criterion::black_box(tree.get(key)); 88 | }) 89 | }); 90 | } 91 | } 92 | 93 | pub fn rand_get_str(c: &mut Criterion) { 94 | let mut group = c.benchmark_group("random_get_str"); 95 | let keys = gen_keys(3, 2, 3); 96 | let cached_keys = gen_cached_keys(3, 2, 3); 97 | group.throughput(Throughput::Elements(1)); 98 | for size in TREE_SIZES { 99 | group.bench_with_input(BenchmarkId::new("cached_keys", size), &size, |b, _size| { 100 | let mut tree = AdaptiveRadixTree::, _>::new(); 101 | for (i, key) in cached_keys.iter().enumerate() { 102 | tree.insert_k(&key.0, i); 103 | } 104 | let mut rng = thread_rng(); 105 | b.iter(|| { 106 | let key = &cached_keys[rng.gen_range(0..keys.len())]; 107 | criterion::black_box(tree.get_k(&key.0)); 108 | }) 109 | }); 110 | } 111 | 112 | for size in TREE_SIZES { 113 | group.bench_with_input( 114 | BenchmarkId::new("uncached_keys", size), 115 | &size, 116 | |b, _size| { 117 | let mut tree = AdaptiveRadixTree::, _>::new(); 118 | for (i, key) in keys.iter().enumerate() { 119 | tree.insert(key, i); 120 | } 121 | let mut rng = thread_rng(); 122 | b.iter(|| { 123 | let key = &keys[rng.gen_range(0..keys.len())]; 124 | criterion::black_box(tree.get(key)); 125 | }) 126 | }, 127 | ); 128 | } 129 | 130 | group.finish(); 131 | } 132 | 133 | pub fn seq_get(c: &mut Criterion) { 134 | for size in TREE_SIZES { 135 | c.bench_with_input(BenchmarkId::new("seq_get", size), &size, |b, size| { 136 | let mut tree = AdaptiveRadixTree::, _>::new(); 137 | for i in 0..*size { 138 | tree.insert(i, i); 139 | } 140 | b.iter_custom(|iters| { 141 | let mut c = 0; 142 | let start = Instant::now(); 143 | for _ in 0..iters { 144 | if c == *size { 145 | c = 0; 146 | } 147 | tree.get(c).unwrap(); 148 | c += 1; 149 | } 150 | start.elapsed() 151 | }) 152 | }); 153 | } 154 | } 155 | 156 | pub fn seq_insert(c: &mut Criterion) { 157 | c.bench_function("seq_insert", |b| { 158 | let mut tree = AdaptiveRadixTree::, _>::new(); 159 | let mut key = 0u64; 160 | b.iter(|| { 161 | tree.insert(key, key); 162 | key += 1; 163 | }) 164 | }); 165 | } 166 | 167 | pub fn seq_remove(c: &mut Criterion) { 168 | for size in TREE_SIZES { 169 | c.bench_with_input(BenchmarkId::new("seq_remove", size), &size, |b, size| { 170 | let mut tree = AdaptiveRadixTree::, _>::new(); 171 | b.iter_custom(|iters| { 172 | for i in 0..*size { 173 | tree.insert(i, i); 174 | } 175 | let mut start = Instant::now(); 176 | let mut cumulative_time = Duration::new(0, 0); 177 | let mut c = 0; 178 | for _ in 0..iters { 179 | if c == *size { 180 | cumulative_time += start.elapsed(); 181 | c = 0; 182 | for i in 0..*size { 183 | tree.insert(i, i); 184 | } 185 | start = Instant::now(); 186 | } 187 | tree.remove(c).unwrap(); 188 | c += 1; 189 | } 190 | cumulative_time += start.elapsed(); 191 | cumulative_time 192 | }) 193 | }); 194 | } 195 | } 196 | 197 | fn gen_keys(l1_prefix: usize, l2_prefix: usize, suffix: usize) -> Vec { 198 | let mut keys = Vec::new(); 199 | let chars: Vec = ('a'..='z').collect(); 200 | for i in 0..chars.len() { 201 | let level1_prefix = chars[i].to_string().repeat(l1_prefix); 202 | for i in 0..chars.len() { 203 | let level2_prefix = chars[i].to_string().repeat(l2_prefix); 204 | let key_prefix = level1_prefix.clone() + &level2_prefix; 205 | for _ in 0..=u8::MAX { 206 | let suffix: String = (0..suffix) 207 | .map(|_| chars[thread_rng().gen_range(0..chars.len())]) 208 | .collect(); 209 | let k = key_prefix.clone() + &suffix; 210 | keys.push(k); 211 | } 212 | } 213 | } 214 | 215 | keys.shuffle(&mut thread_rng()); 216 | keys 217 | } 218 | 219 | fn gen_cached_keys( 220 | l1_prefix: usize, 221 | l2_prefix: usize, 222 | suffix: usize, 223 | ) -> Vec<(ArrayKey<16>, String)> { 224 | let mut keys = Vec::new(); 225 | let chars: Vec = ('a'..='z').collect(); 226 | for i in 0..chars.len() { 227 | let level1_prefix = chars[i].to_string().repeat(l1_prefix); 228 | for i in 0..chars.len() { 229 | let level2_prefix = chars[i].to_string().repeat(l2_prefix); 230 | let key_prefix = level1_prefix.clone() + &level2_prefix; 231 | for _ in 0..=u8::MAX { 232 | let suffix: String = (0..suffix) 233 | .map(|_| chars[thread_rng().gen_range(0..chars.len())]) 234 | .collect(); 235 | let string = key_prefix.clone() + &suffix; 236 | let k = string.clone().into(); 237 | keys.push((k, string)); 238 | } 239 | } 240 | } 241 | 242 | keys.shuffle(&mut thread_rng()); 243 | keys 244 | } 245 | 246 | criterion_group!( 247 | rand_benches, 248 | rand_get, 249 | rand_get_str, 250 | rand_insert, 251 | rand_remove 252 | ); 253 | criterion_group!(seq_benches, seq_get, seq_insert, seq_remove); 254 | criterion_main!(seq_benches, rand_benches); 255 | -------------------------------------------------------------------------------- /rart/benches/art_compare_bench.rs: -------------------------------------------------------------------------------- 1 | /// Comparitive benchmarks showing performnce of the radix tree in comparison to hashes and btrees 2 | /// for various numbers of keys and for various operations. 3 | /// Takes a long time to run. 4 | use std::collections::{BTreeMap, HashMap}; 5 | use std::time::Instant; 6 | 7 | use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; 8 | use rand::prelude::SliceRandom; 9 | use rand::{thread_rng, Rng}; 10 | 11 | use rart::keys::array_key::ArrayKey; 12 | 13 | use rart::tree::AdaptiveRadixTree; 14 | use rart::TreeTrait; 15 | 16 | const TREE_SIZES: [u64; 4] = [1 << 15, 1 << 20, 1 << 22, 1 << 24]; 17 | 18 | pub fn seq_insert(c: &mut Criterion) { 19 | let mut group = c.benchmark_group("seq_insert"); 20 | group.throughput(Throughput::Elements(1)); 21 | group.bench_function("seq_insert", |b| { 22 | let mut tree = AdaptiveRadixTree::, _>::new(); 23 | let mut key = 0u64; 24 | b.iter(|| { 25 | tree.insert(key, key); 26 | key += 1; 27 | }) 28 | }); 29 | 30 | group.throughput(Throughput::Elements(1)); 31 | group.bench_function("seq_insert_hash", |b| { 32 | let mut tree = HashMap::new(); 33 | let mut key = 0u64; 34 | b.iter(|| { 35 | tree.insert(key, key); 36 | key += 1; 37 | }) 38 | }); 39 | 40 | group.throughput(Throughput::Elements(1)); 41 | group.bench_function("seq_insert_btree", |b| { 42 | let mut tree = BTreeMap::new(); 43 | let mut key = 0u64; 44 | b.iter(|| { 45 | tree.insert(key, key); 46 | key += 1; 47 | }) 48 | }); 49 | 50 | group.finish(); 51 | } 52 | 53 | pub fn rand_insert(c: &mut Criterion) { 54 | let mut group = c.benchmark_group("rand_insert"); 55 | group.throughput(Throughput::Elements(1)); 56 | 57 | let keys = gen_keys(3, 2, 3); 58 | let cached_keys = gen_cached_keys(3, 2, 3); 59 | 60 | group.bench_function("art_cached_keys", |b| { 61 | let mut tree = AdaptiveRadixTree::, _>::new(); 62 | let mut rng = thread_rng(); 63 | b.iter(|| { 64 | let key = &cached_keys[rng.gen_range(0..cached_keys.len())]; 65 | tree.insert_k(&key.0, key.1.clone()); 66 | }) 67 | }); 68 | 69 | group.bench_function("art", |b| { 70 | let mut tree = AdaptiveRadixTree::, _>::new(); 71 | let mut rng = thread_rng(); 72 | b.iter(|| { 73 | let key = &keys[rng.gen_range(0..keys.len())]; 74 | tree.insert(key, key.clone()); 75 | }) 76 | }); 77 | 78 | group.bench_function("hash", |b| { 79 | let mut tree = HashMap::new(); 80 | let mut rng = thread_rng(); 81 | b.iter(|| { 82 | let key = &keys[rng.gen_range(0..keys.len())]; 83 | tree.insert(key, key.clone()); 84 | }) 85 | }); 86 | 87 | group.bench_function("btree", |b| { 88 | let mut tree = BTreeMap::new(); 89 | let mut rng = thread_rng(); 90 | b.iter(|| { 91 | let key = &keys[rng.gen_range(0..keys.len())]; 92 | tree.insert(key, key.clone()); 93 | }) 94 | }); 95 | group.finish(); 96 | } 97 | 98 | pub fn seq_delete(c: &mut Criterion) { 99 | let mut group = c.benchmark_group("seq_delete"); 100 | group.throughput(Throughput::Elements(1)); 101 | group.bench_function("art", |b| { 102 | let mut tree = AdaptiveRadixTree::, _>::new(); 103 | b.iter_custom(|iters| { 104 | for i in 0..iters { 105 | tree.insert(i, i); 106 | } 107 | let start = Instant::now(); 108 | for i in 0..iters { 109 | tree.remove(i); 110 | } 111 | start.elapsed() 112 | }) 113 | }); 114 | 115 | group.bench_function("hash", |b| { 116 | let mut tree = HashMap::new(); 117 | b.iter_custom(|iters| { 118 | for i in 0..iters { 119 | tree.insert(i, i); 120 | } 121 | let start = Instant::now(); 122 | for i in 0..iters { 123 | tree.remove(&i); 124 | } 125 | start.elapsed() 126 | }) 127 | }); 128 | 129 | group.bench_function("btree", |b| { 130 | let mut tree = BTreeMap::new(); 131 | b.iter_custom(|iters| { 132 | for i in 0..iters { 133 | tree.insert(i, i); 134 | } 135 | let start = Instant::now(); 136 | for i in 0..iters { 137 | tree.remove(&i); 138 | } 139 | start.elapsed() 140 | }) 141 | }); 142 | 143 | group.finish(); 144 | } 145 | 146 | pub fn rand_delete(c: &mut Criterion) { 147 | let mut group = c.benchmark_group("rand_delete"); 148 | let keys = gen_keys(3, 2, 3); 149 | let cached_keys = gen_cached_keys(3, 2, 3); 150 | 151 | group.throughput(Throughput::Elements(1)); 152 | group.bench_function("art", |b| { 153 | let mut tree = AdaptiveRadixTree::, _>::new(); 154 | let mut rng = thread_rng(); 155 | for key in &keys { 156 | tree.insert(key, key); 157 | } 158 | b.iter(|| { 159 | let key = &keys[rng.gen_range(0..keys.len())]; 160 | criterion::black_box(tree.remove(key)); 161 | }) 162 | }); 163 | 164 | group.bench_function("art_cached_keys", |b| { 165 | let mut tree = AdaptiveRadixTree::, _>::new(); 166 | let mut rng = thread_rng(); 167 | for key in &cached_keys { 168 | tree.insert_k(&key.0, key.1.clone()); 169 | } 170 | b.iter(|| { 171 | let key = &cached_keys[rng.gen_range(0..keys.len())]; 172 | criterion::black_box(tree.remove_k(&key.0)); 173 | }) 174 | }); 175 | group.bench_function("hash", |b| { 176 | let mut tree = HashMap::new(); 177 | let mut rng = thread_rng(); 178 | for key in &keys { 179 | tree.insert(key, key); 180 | } 181 | b.iter(|| { 182 | let key = &keys[rng.gen_range(0..keys.len())]; 183 | criterion::black_box(tree.remove(key)); 184 | }) 185 | }); 186 | 187 | group.bench_function("btree", |b| { 188 | let mut tree = BTreeMap::new(); 189 | let mut rng = thread_rng(); 190 | for key in &keys { 191 | tree.insert(key, key); 192 | } 193 | b.iter(|| { 194 | let key = &keys[rng.gen_range(0..keys.len())]; 195 | criterion::black_box(tree.remove(key)); 196 | }) 197 | }); 198 | group.finish(); 199 | } 200 | 201 | pub fn rand_get(c: &mut Criterion) { 202 | let mut group = c.benchmark_group("random_get"); 203 | 204 | group.throughput(Throughput::Elements(1)); 205 | { 206 | for size in TREE_SIZES { 207 | group.bench_with_input(BenchmarkId::new("art", size), &size, |b, size| { 208 | let mut tree = AdaptiveRadixTree::, _>::new(); 209 | for i in 0..*size { 210 | tree.insert(i, i); 211 | } 212 | let mut rng = thread_rng(); 213 | b.iter(|| { 214 | let key = rng.gen_range(0..*size); 215 | criterion::black_box(tree.get(key)); 216 | }) 217 | }); 218 | } 219 | } 220 | group.throughput(Throughput::Elements(1)); 221 | { 222 | for size in TREE_SIZES { 223 | group.bench_with_input(BenchmarkId::new("btree", size), &size, |b, size| { 224 | let mut tree = BTreeMap::new(); 225 | for i in 0..*size { 226 | tree.insert(i, i); 227 | } 228 | let mut rng = thread_rng(); 229 | b.iter(|| { 230 | let key = rng.gen_range(0..*size); 231 | criterion::black_box(tree.get(&key)); 232 | }) 233 | }); 234 | } 235 | } 236 | 237 | { 238 | for size in TREE_SIZES { 239 | group.bench_with_input(BenchmarkId::new("hash", size), &size, |b, size| { 240 | let mut tree = HashMap::new(); 241 | for i in 0..*size { 242 | tree.insert(i, i); 243 | } 244 | let mut rng = thread_rng(); 245 | b.iter(|| { 246 | let key = rng.gen_range(0..*size); 247 | criterion::black_box(tree.get(&key)); 248 | }) 249 | }); 250 | } 251 | } 252 | 253 | group.finish(); 254 | } 255 | 256 | pub fn rand_get_str(c: &mut Criterion) { 257 | let mut group = c.benchmark_group("random_get_str"); 258 | let keys = gen_keys(3, 2, 3); 259 | let cached_keys = gen_cached_keys(3, 2, 3); 260 | 261 | { 262 | for size in TREE_SIZES { 263 | group.bench_with_input(BenchmarkId::new("art", size), &size, |b, _size| { 264 | let mut tree = AdaptiveRadixTree::, _>::new(); 265 | for (i, key) in keys.iter().enumerate() { 266 | tree.insert(key, i); 267 | } 268 | let mut rng = thread_rng(); 269 | b.iter(|| { 270 | let key = &keys[rng.gen_range(0..keys.len())]; 271 | criterion::black_box(tree.get(key)); 272 | }) 273 | }); 274 | } 275 | } 276 | 277 | group.throughput(Throughput::Elements(1)); 278 | { 279 | for size in TREE_SIZES { 280 | group.bench_with_input( 281 | BenchmarkId::new("art_cached_keys", size), 282 | &size, 283 | |b, _size| { 284 | let mut tree = AdaptiveRadixTree::, _>::new(); 285 | for (i, key) in cached_keys.iter().enumerate() { 286 | tree.insert_k(&key.0, i); 287 | } 288 | let mut rng = thread_rng(); 289 | b.iter(|| { 290 | let key = &cached_keys[rng.gen_range(0..keys.len())]; 291 | criterion::black_box(tree.get_k(&key.0)); 292 | }) 293 | }, 294 | ); 295 | } 296 | } 297 | 298 | { 299 | for size in TREE_SIZES { 300 | group.bench_with_input(BenchmarkId::new("btree", size), &size, |b, _size| { 301 | let mut tree = BTreeMap::new(); 302 | for (i, key) in keys.iter().enumerate() { 303 | tree.insert(key, i); 304 | } 305 | let mut rng = thread_rng(); 306 | b.iter(|| { 307 | let key = &keys[rng.gen_range(0..keys.len())]; 308 | criterion::black_box(tree.get(key)); 309 | }) 310 | }); 311 | } 312 | } 313 | 314 | { 315 | for size in TREE_SIZES { 316 | group.bench_with_input(BenchmarkId::new("hash", size), &size, |b, _size| { 317 | let mut tree = HashMap::new(); 318 | for (i, key) in keys.iter().enumerate() { 319 | tree.insert(key, i); 320 | } 321 | let mut rng = thread_rng(); 322 | b.iter(|| { 323 | let key = &keys[rng.gen_range(0..keys.len())]; 324 | criterion::black_box(tree.get(key)); 325 | }) 326 | }); 327 | } 328 | } 329 | 330 | group.finish(); 331 | } 332 | 333 | pub fn seq_get(c: &mut Criterion) { 334 | let mut group = c.benchmark_group("seq_get"); 335 | 336 | group.throughput(Throughput::Elements(1)); 337 | { 338 | for size in TREE_SIZES { 339 | group.bench_with_input(BenchmarkId::new("art", size), &size, |b, size| { 340 | let mut tree = AdaptiveRadixTree::, _>::new(); 341 | for i in 0..*size { 342 | tree.insert(i, i); 343 | } 344 | let mut key = 0u64; 345 | b.iter(|| { 346 | criterion::black_box(tree.get(key)); 347 | key += 1; 348 | }) 349 | }); 350 | } 351 | } 352 | { 353 | for size in TREE_SIZES { 354 | group.bench_with_input(BenchmarkId::new("btree", size), &size, |b, size| { 355 | let mut tree = BTreeMap::new(); 356 | for i in 0..*size { 357 | tree.insert(i, i); 358 | } 359 | let mut key = 0u64; 360 | b.iter(|| { 361 | criterion::black_box(tree.get(&key)); 362 | key += 1; 363 | }) 364 | }); 365 | } 366 | } 367 | 368 | { 369 | for size in TREE_SIZES { 370 | group.bench_with_input(BenchmarkId::new("hash", size), &size, |b, size| { 371 | let mut tree = HashMap::new(); 372 | for i in 0..*size { 373 | tree.insert(i, i); 374 | } 375 | let mut key = 0u64; 376 | b.iter(|| { 377 | criterion::black_box(tree.get(&key)); 378 | key += 1; 379 | }) 380 | }); 381 | } 382 | } 383 | 384 | group.finish(); 385 | } 386 | 387 | fn gen_keys(l1_prefix: usize, l2_prefix: usize, suffix: usize) -> Vec { 388 | let mut keys = Vec::new(); 389 | let chars: Vec = ('a'..='z').collect(); 390 | for i in 0..chars.len() { 391 | let level1_prefix = chars[i].to_string().repeat(l1_prefix); 392 | for i in 0..chars.len() { 393 | let level2_prefix = chars[i].to_string().repeat(l2_prefix); 394 | let key_prefix = level1_prefix.clone() + &level2_prefix; 395 | for _ in 0..=u8::MAX { 396 | let suffix: String = (0..suffix) 397 | .map(|_| chars[thread_rng().gen_range(0..chars.len())]) 398 | .collect(); 399 | let k = key_prefix.clone() + &suffix; 400 | keys.push(k); 401 | } 402 | } 403 | } 404 | 405 | keys.shuffle(&mut thread_rng()); 406 | keys 407 | } 408 | 409 | fn gen_cached_keys( 410 | l1_prefix: usize, 411 | l2_prefix: usize, 412 | suffix: usize, 413 | ) -> Vec<(ArrayKey<16>, String)> { 414 | let mut keys = Vec::new(); 415 | let chars: Vec = ('a'..='z').collect(); 416 | for i in 0..chars.len() { 417 | let level1_prefix = chars[i].to_string().repeat(l1_prefix); 418 | for i in 0..chars.len() { 419 | let level2_prefix = chars[i].to_string().repeat(l2_prefix); 420 | let key_prefix = level1_prefix.clone() + &level2_prefix; 421 | for _ in 0..=u8::MAX { 422 | let suffix: String = (0..suffix) 423 | .map(|_| chars[thread_rng().gen_range(0..chars.len())]) 424 | .collect(); 425 | let string = key_prefix.clone() + &suffix; 426 | let k = string.clone().into(); 427 | keys.push((k, string)); 428 | } 429 | } 430 | } 431 | 432 | keys.shuffle(&mut thread_rng()); 433 | keys 434 | } 435 | 436 | criterion_group!(delete_benches, seq_delete, rand_delete); 437 | criterion_group!(insert_benches, seq_insert, rand_insert); 438 | criterion_group!(retr_benches, seq_get, rand_get, rand_get_str); 439 | criterion_main!(retr_benches, insert_benches, delete_benches); 440 | -------------------------------------------------------------------------------- /rart/benches/art_iai_bench.rs: -------------------------------------------------------------------------------- 1 | use iai_callgrind::{black_box, main}; 2 | use paste::paste; 3 | use rand::{thread_rng, Rng}; 4 | 5 | use rart::keys::array_key::ArrayKey; 6 | use rart::tree::AdaptiveRadixTree; 7 | use rart::TreeTrait; 8 | 9 | const TREE_SIZES: [u64; 4] = [1 << 14, 1 << 16, 1 << 18, 1 << 20]; 10 | 11 | #[export_name = "tree_setup"] 12 | fn setup_tree(n: usize) -> AdaptiveRadixTree, u64> { 13 | let mut tree = AdaptiveRadixTree::, _>::new(); 14 | 15 | for i in 0..TREE_SIZES[n] { 16 | tree.insert(i, i); 17 | } 18 | tree 19 | } 20 | 21 | #[inline(never)] 22 | fn benchmark_seq_insert(n: usize) { 23 | let mut tree = AdaptiveRadixTree::, _>::new(); 24 | 25 | for i in 0..TREE_SIZES[n] { 26 | black_box(tree.insert(i, i)); 27 | } 28 | } 29 | 30 | #[inline(never)] 31 | fn benchmark_rnd_insert(n: usize) { 32 | let mut tree = AdaptiveRadixTree::, _>::new(); 33 | 34 | let mut rng = thread_rng(); 35 | for _ in 0..TREE_SIZES[n] { 36 | black_box(tree.insert(rng.gen_range(0..TREE_SIZES[n]), 0)); 37 | } 38 | } 39 | 40 | #[inline(never)] 41 | fn benchmark_seq_get(n: usize) { 42 | let tree = black_box(setup_tree(n)); 43 | 44 | for i in 0..TREE_SIZES[n] { 45 | black_box(tree.get(i)).unwrap(); 46 | } 47 | } 48 | 49 | #[inline(never)] 50 | fn benchmark_rnd_get(tree_size: usize) { 51 | let tree = black_box(setup_tree(tree_size)); 52 | 53 | let mut rng = thread_rng(); 54 | for _ in 0..1_000_000 { 55 | black_box(tree.get(rng.gen_range(0..TREE_SIZES[tree_size])).unwrap()); 56 | } 57 | } 58 | 59 | // Simple macro to produce 4 versions of each benchmark function 60 | macro_rules! mk_benchmark { 61 | ($name:ident { $($n:literal),* }) => { 62 | paste! { 63 | $( 64 | #[inline(never)] 65 | 66 | fn [<$name _ $n>]() { 67 | $name($n) 68 | } )* 69 | } 70 | }; 71 | } 72 | 73 | mk_benchmark!(benchmark_seq_insert { 0, 1, 2, 3 }); 74 | mk_benchmark!(benchmark_rnd_insert { 0, 1, 2, 3 }); 75 | mk_benchmark!(benchmark_seq_get { 0, 1, 2, 3 }); 76 | mk_benchmark!(benchmark_rnd_get { 0, 1, 2, 3 }); 77 | 78 | main!( 79 | callgrind_args = "toggle-collect=tree_setup"; 80 | functions = benchmark_seq_get_0, benchmark_seq_get_1, benchmark_seq_get_2, benchmark_seq_get_3, 81 | benchmark_rnd_get_0, benchmark_rnd_get_1, benchmark_rnd_get_2, benchmark_rnd_get_3, 82 | benchmark_seq_insert_0, benchmark_seq_insert_1, benchmark_seq_insert_2, benchmark_seq_insert_3, 83 | benchmark_rnd_insert_0, benchmark_rnd_insert_1, benchmark_rnd_insert_2, benchmark_rnd_insert_3 84 | ); 85 | -------------------------------------------------------------------------------- /rart/benches/node_mapping_iai_microbenches.rs: -------------------------------------------------------------------------------- 1 | use iai_callgrind::main; 2 | use std::collections::HashSet; 3 | 4 | use rart::mapping::direct_mapping::DirectMapping; 5 | use rart::mapping::indexed_mapping::IndexedMapping; 6 | use rart::mapping::keyed_mapping::KeyedMapping; 7 | use rart::mapping::sorted_keyed_mapping::SortedKeyedMapping; 8 | use rart::mapping::NodeMapping; 9 | use rart::utils::bitset::{Bitset16, Bitset64, Bitset8}; 10 | 11 | type KeyMapping16_16x1 = KeyedMapping>; 12 | type KeyMapping4 = KeyedMapping>; 13 | 14 | #[inline(never)] 15 | fn make_mapping_sets(iters: u64) -> Vec<(MappingType, Vec)> 16 | where 17 | MappingType: NodeMapping + Default, 18 | { 19 | // Break iters into 256-chunks, and prepare child sets and mappings for each chunk. 20 | let mut mapping_set = Vec::with_capacity((iters / (WIDTH as u64)) as usize); 21 | for _ in 0..iters / (WIDTH as u64) { 22 | // Produce a random set of unique child keys to add, WIDTH-wide. The same child cannot 23 | // be present twice in the same set. 24 | let mut child_hash_set = HashSet::with_capacity(WIDTH); 25 | while child_hash_set.len() < WIDTH { 26 | child_hash_set.insert(rand::random::()); 27 | } 28 | let child_set = child_hash_set.into_iter().collect::>(); 29 | mapping_set.push((MappingType::default(), child_set)); 30 | } 31 | mapping_set 32 | } 33 | 34 | #[export_name = "setup_seek"] 35 | #[inline(never)] 36 | fn setup_seek_bench(iters: u64) -> Vec<(MappingType, Vec)> 37 | where 38 | MappingType: NodeMapping + Default, 39 | { 40 | let _mapping_set = make_mapping_sets::(iters); 41 | let mut mapping_set = make_mapping_sets::(iters); 42 | for (ref mut mapping, child_set) in &mut mapping_set { 43 | for child in child_set { 44 | mapping.add_child(*child, 0u64); 45 | } 46 | } 47 | mapping_set 48 | } 49 | 50 | #[inline(never)] 51 | fn benched_seek_child(iters: u64) 52 | where 53 | MappingType: NodeMapping + Default, 54 | { 55 | let mut mapping_set = make_mapping_sets::(iters); 56 | // Fill all the sets. 57 | for (ref mut mapping, child_set) in &mut mapping_set { 58 | for child in child_set { 59 | mapping.add_child(*child, 0u64); 60 | } 61 | } 62 | 63 | for (ref mut mapping, child_set) in &mut mapping_set { 64 | for child in child_set { 65 | mapping.seek_child(*child); 66 | } 67 | } 68 | } 69 | 70 | #[inline(never)] 71 | fn bench_node_256_seek() { 72 | benched_seek_child::<256, DirectMapping>(1 << 18); 73 | } 74 | 75 | #[inline(never)] 76 | fn bench_node_48_seek() { 77 | benched_seek_child::<48, IndexedMapping>>(1 << 18); 78 | } 79 | 80 | #[inline(never)] 81 | fn bench_node_16_seek() { 82 | benched_seek_child::<16, KeyMapping16_16x1>(1 << 18); 83 | } 84 | 85 | #[inline(never)] 86 | fn bench_node_16_sorted_seek() { 87 | benched_seek_child::<16, SortedKeyedMapping>(1 << 18); 88 | } 89 | 90 | #[inline(never)] 91 | fn bench_node_4_seek() { 92 | benched_seek_child::<4, KeyMapping4>(1 << 18); 93 | } 94 | 95 | main!( 96 | callgrind_args = "toggle-collect=setup_sets,setup_seek"; 97 | functions = bench_node_256_seek, bench_node_48_seek, bench_node_16_seek, bench_node_16_sorted_seek, bench_node_4_seek 98 | ); 99 | -------------------------------------------------------------------------------- /rart/benches/node_mapping_microbenches.rs: -------------------------------------------------------------------------------- 1 | /// Microbenches for the specific node mapping types and arrangements. 2 | /// Takes quite a while to run. 3 | use std::collections::HashSet; 4 | use std::time::{Duration, Instant}; 5 | 6 | use criterion::{criterion_group, criterion_main, Criterion, Throughput}; 7 | 8 | use rart::mapping::direct_mapping::DirectMapping; 9 | use rart::mapping::indexed_mapping::IndexedMapping; 10 | use rart::mapping::keyed_mapping::KeyedMapping; 11 | use rart::mapping::sorted_keyed_mapping::SortedKeyedMapping; 12 | use rart::mapping::NodeMapping; 13 | use rart::utils::bitset::{Bitset16, Bitset32, Bitset64, Bitset8, BitsetTrait}; 14 | 15 | type KeyMapping32_32x1 = KeyedMapping>; 16 | type KeyMapping32_16x2 = KeyedMapping>; 17 | type KeyMapping32_8x4 = KeyedMapping>; 18 | 19 | type KeyMapping16_16x1 = KeyedMapping>; 20 | type KeyMapping16_8x2 = KeyedMapping>; 21 | 22 | type KeyMapping4 = KeyedMapping>; 23 | 24 | fn benched_grow_keyed_node( 25 | iters: u64, 26 | ) -> Duration 27 | where 28 | FromBitset: BitsetTrait, 29 | ToBitset: BitsetTrait, 30 | { 31 | // Create the nodes 32 | let mut mapping_set = 33 | make_mapping_sets::>(iters); 34 | 35 | // Fill them up with children 36 | for (ref mut mapping, child_set) in &mut mapping_set { 37 | for child in child_set { 38 | mapping.add_child(*child, 0u64); 39 | } 40 | } 41 | 42 | // Now go through and grow each node. 43 | let start = Instant::now(); 44 | for (ref mut mapping, _child_set) in &mut mapping_set { 45 | let _new: KeyedMapping = KeyedMapping::from_resized_grow(mapping); 46 | } 47 | start.elapsed() 48 | } 49 | 50 | fn benched_grow_keyed_to_index< 51 | const FROM_WIDTH: usize, 52 | FromBitset, 53 | const TO_WIDTH: usize, 54 | ToBitset, 55 | >( 56 | iters: u64, 57 | ) -> Duration 58 | where 59 | FromBitset: BitsetTrait, 60 | ToBitset: BitsetTrait, 61 | { 62 | // Create the nodes 63 | let mut mapping_set = 64 | make_mapping_sets::>(iters); 65 | 66 | // Fill them up with children 67 | for (ref mut mapping, child_set) in &mut mapping_set { 68 | for child in child_set { 69 | mapping.add_child(*child, 0u64); 70 | } 71 | } 72 | 73 | let start = Instant::now(); 74 | for (ref mut mapping, _child_set) in &mut mapping_set { 75 | let _new: IndexedMapping = IndexedMapping::from_keyed(mapping); 76 | } 77 | start.elapsed() 78 | } 79 | 80 | fn bench_grow_indexed_to_direct( 81 | iters: u64, 82 | ) -> Duration { 83 | // Create the nodes 84 | let mut mapping_set = 85 | make_mapping_sets::>(iters); 86 | 87 | // Fill them up with children 88 | for (ref mut mapping, child_set) in &mut mapping_set { 89 | for child in child_set { 90 | mapping.add_child(*child, 0u64); 91 | } 92 | } 93 | 94 | let start = Instant::now(); 95 | for (ref mut mapping, _child_set) in &mut mapping_set { 96 | let _new: DirectMapping = DirectMapping::from_indexed(mapping); 97 | } 98 | start.elapsed() 99 | } 100 | 101 | fn benched_add_child(iters: u64) -> Duration 102 | where 103 | MappingType: NodeMapping + Default, 104 | { 105 | let mut mapping_set = make_mapping_sets::(iters); 106 | let start = Instant::now(); 107 | for (ref mut mapping, child_set) in &mut mapping_set { 108 | for child in child_set { 109 | mapping.add_child(*child, 0u64); 110 | } 111 | } 112 | start.elapsed() 113 | } 114 | 115 | fn benched_del_child(iters: u64) -> Duration 116 | where 117 | MappingType: NodeMapping + Default, 118 | { 119 | let mut mapping_set = make_mapping_sets::(iters); 120 | // Fill all the sets. 121 | for (ref mut mapping, child_set) in &mut mapping_set { 122 | for child in child_set { 123 | mapping.add_child(*child, 0u64); 124 | } 125 | } 126 | 127 | // Then time the deletion only. 128 | let start = Instant::now(); 129 | for (ref mut mapping, child_set) in &mut mapping_set { 130 | for child in child_set { 131 | mapping.delete_child(*child); 132 | } 133 | } 134 | start.elapsed() 135 | } 136 | 137 | fn benched_seek_child(iters: u64) -> Duration 138 | where 139 | MappingType: NodeMapping + Default, 140 | { 141 | let mut mapping_set = make_mapping_sets::(iters); 142 | // Fill all the sets. 143 | for (ref mut mapping, child_set) in &mut mapping_set { 144 | for child in child_set { 145 | mapping.add_child(*child, 0u64); 146 | } 147 | } 148 | 149 | // Then time the find only. 150 | let start = Instant::now(); 151 | for (ref mut mapping, child_set) in &mut mapping_set { 152 | for child in child_set { 153 | mapping.seek_child(*child); 154 | } 155 | } 156 | start.elapsed() 157 | } 158 | 159 | fn make_mapping_sets(iters: u64) -> Vec<(MappingType, Vec)> 160 | where 161 | MappingType: NodeMapping + Default, 162 | { 163 | // Break iters into 256-chunks, and prepare child sets and mappings for each chunk. 164 | let mut mapping_set = Vec::with_capacity((iters / (WIDTH as u64)) as usize); 165 | for _ in 0..iters / (WIDTH as u64) { 166 | // Produce a random set of unique child keys to add, WIDTH-wide. The same child cannot 167 | // be present twice in the same set. 168 | let mut child_hash_set = HashSet::with_capacity(WIDTH); 169 | while child_hash_set.len() < WIDTH { 170 | child_hash_set.insert(rand::random::()); 171 | } 172 | let child_set = child_hash_set.into_iter().collect::>(); 173 | mapping_set.push((MappingType::default(), child_set)); 174 | } 175 | mapping_set 176 | } 177 | 178 | pub fn grow_node(c: &mut Criterion) { 179 | let mut group = c.benchmark_group("grow_node"); 180 | group.throughput(Throughput::Elements(1)); 181 | group.sample_size(4096); 182 | group.measurement_time(Duration::from_secs(10)); 183 | 184 | group.bench_function("n4_to_n16_16x1", |b| { 185 | b.iter_custom(benched_grow_keyed_node::<4, Bitset8<1>, 16, Bitset16<1>>); 186 | }); 187 | group.bench_function("n4_to_n16_8x2", |b| { 188 | b.iter_custom(benched_grow_keyed_node::<4, Bitset8<1>, 16, Bitset8<2>>); 189 | }); 190 | 191 | group.bench_function("n16_to_n32_32x1", |b| { 192 | b.iter_custom(benched_grow_keyed_node::<4, Bitset8<1>, 16, Bitset32<1>>); 193 | }); 194 | group.bench_function("n16_to_n32_16x2", |b| { 195 | b.iter_custom(benched_grow_keyed_node::<4, Bitset8<1>, 16, Bitset16<2>>); 196 | }); 197 | group.bench_function("n16_to_n32_8x4", |b| { 198 | b.iter_custom(benched_grow_keyed_node::<4, Bitset8<1>, 16, Bitset8<4>>); 199 | }); 200 | 201 | group.bench_function("n32_32x1_to_n48_16x3", |b| { 202 | b.iter_custom(benched_grow_keyed_to_index::<32, Bitset32<1>, 48, Bitset16<3>>); 203 | }); 204 | 205 | group.bench_function("n48_16x3_to_direct", |b| { 206 | b.iter_custom(bench_grow_indexed_to_direct::<48, Bitset16<3>>); 207 | }); 208 | } 209 | 210 | pub fn add_child(c: &mut Criterion) { 211 | let mut group = c.benchmark_group("add_child"); 212 | group.throughput(Throughput::Elements(1)); 213 | group.sample_size(4096); 214 | group.measurement_time(Duration::from_secs(10)); 215 | 216 | group.bench_function("direct", |b| { 217 | b.iter_custom(benched_add_child::<256, DirectMapping>); 218 | }); 219 | 220 | group.bench_function("indexed48_16x3", |b| { 221 | b.iter_custom(benched_add_child::<48, IndexedMapping>>) 222 | }); 223 | 224 | group.bench_function("indexed48_8x6", |b| { 225 | b.iter_custom(benched_add_child::<48, IndexedMapping>>) 226 | }); 227 | 228 | group.bench_function("indexed48_32x2", |b| { 229 | b.iter_custom(benched_add_child::<48, IndexedMapping>>) 230 | }); 231 | 232 | group.bench_function("indexed48_64x1", |b| { 233 | b.iter_custom(benched_add_child::<48, IndexedMapping>>) 234 | }); 235 | 236 | group.bench_function("keyed32_32x1", |b| { 237 | b.iter_custom(benched_add_child::<32, KeyMapping32_32x1>) 238 | }); 239 | 240 | group.bench_function("keyed32_16x2", |b| { 241 | b.iter_custom(benched_add_child::<32, KeyMapping32_16x2>) 242 | }); 243 | 244 | group.bench_function("keyed32_8x4", |b| { 245 | b.iter_custom(benched_add_child::<32, KeyMapping32_8x4>) 246 | }); 247 | 248 | group.bench_function("keyed16_16x1", |b| { 249 | b.iter_custom(benched_add_child::<16, KeyMapping16_16x1>) 250 | }); 251 | 252 | group.bench_function("keyed16_8x2", |b| { 253 | b.iter_custom(benched_add_child::<16, KeyMapping16_8x2>) 254 | }); 255 | 256 | group.bench_function("keyed4", |b| { 257 | b.iter_custom(benched_add_child::<4, KeyMapping4>) 258 | }); 259 | 260 | group.bench_function("sorted_keyed32", |b| { 261 | b.iter_custom(benched_add_child::<32, SortedKeyedMapping>) 262 | }); 263 | 264 | group.bench_function("sorted_keyed16", |b| { 265 | b.iter_custom(benched_add_child::<16, SortedKeyedMapping>) 266 | }); 267 | 268 | group.bench_function("sorted_keyed4", |b| { 269 | b.iter_custom(benched_add_child::<4, SortedKeyedMapping>) 270 | }); 271 | 272 | group.finish(); 273 | } 274 | 275 | pub fn del_child(c: &mut Criterion) { 276 | let mut group = c.benchmark_group("del_child"); 277 | group.throughput(Throughput::Elements(1)); 278 | group.sample_size(4096); 279 | group.measurement_time(Duration::from_secs(10)); 280 | 281 | group.bench_function("direct", |b| { 282 | b.iter_custom(benched_del_child::<256, DirectMapping>); 283 | }); 284 | 285 | group.bench_function("indexed48_16x3", |b| { 286 | b.iter_custom(benched_del_child::<48, IndexedMapping>>) 287 | }); 288 | 289 | group.bench_function("indexed48_8x6", |b| { 290 | b.iter_custom(benched_del_child::<48, IndexedMapping>>) 291 | }); 292 | 293 | group.bench_function("indexed48_32x2", |b| { 294 | b.iter_custom(benched_del_child::<48, IndexedMapping>>) 295 | }); 296 | 297 | group.bench_function("indexed48_64x1", |b| { 298 | b.iter_custom(benched_del_child::<48, IndexedMapping>>) 299 | }); 300 | 301 | group.bench_function("keyed32_32x1", |b| { 302 | b.iter_custom(benched_del_child::<32, KeyMapping32_32x1>) 303 | }); 304 | 305 | group.bench_function("keyed32_16x2", |b| { 306 | b.iter_custom(benched_del_child::<32, KeyMapping32_16x2>) 307 | }); 308 | 309 | group.bench_function("keyed32_8x4", |b| { 310 | b.iter_custom(benched_del_child::<32, KeyMapping32_8x4>) 311 | }); 312 | 313 | group.bench_function("keyed16_16x1", |b| { 314 | b.iter_custom(benched_del_child::<16, KeyMapping16_16x1>) 315 | }); 316 | 317 | group.bench_function("keyed16_8x2", |b| { 318 | b.iter_custom(benched_del_child::<16, KeyMapping16_8x2>) 319 | }); 320 | group.bench_function("keyed4", |b| { 321 | b.iter_custom(benched_del_child::<4, KeyMapping4>) 322 | }); 323 | 324 | group.bench_function("sorted_keyed32", |b| { 325 | b.iter_custom(benched_del_child::<32, SortedKeyedMapping>) 326 | }); 327 | 328 | group.bench_function("sorted_keyed16", |b| { 329 | b.iter_custom(benched_del_child::<16, SortedKeyedMapping>) 330 | }); 331 | 332 | group.bench_function("sorted_keyed4", |b| { 333 | b.iter_custom(benched_del_child::<4, SortedKeyedMapping>) 334 | }); 335 | 336 | group.finish(); 337 | } 338 | 339 | pub fn seek_child(c: &mut Criterion) { 340 | let mut group = c.benchmark_group("seek_child"); 341 | group.throughput(Throughput::Elements(1)); 342 | group.sample_size(4096); 343 | group.measurement_time(Duration::from_secs(10)); 344 | 345 | group.bench_function("direct", |b| { 346 | b.iter_custom(benched_seek_child::<256, DirectMapping>); 347 | }); 348 | 349 | group.bench_function("indexed48_16x3", |b| { 350 | b.iter_custom(benched_seek_child::<48, IndexedMapping>>) 351 | }); 352 | 353 | group.bench_function("indexed48_8x6", |b| { 354 | b.iter_custom(benched_seek_child::<48, IndexedMapping>>) 355 | }); 356 | 357 | group.bench_function("indexed48_32x2", |b| { 358 | b.iter_custom(benched_seek_child::<48, IndexedMapping>>) 359 | }); 360 | 361 | group.bench_function("indexed48_64x1", |b| { 362 | b.iter_custom(benched_seek_child::<48, IndexedMapping>>) 363 | }); 364 | 365 | group.bench_function("keyed32_32x1", |b| { 366 | b.iter_custom(benched_seek_child::<32, KeyMapping32_32x1>) 367 | }); 368 | 369 | group.bench_function("keyed32_16x2", |b| { 370 | b.iter_custom(benched_seek_child::<32, KeyMapping32_16x2>) 371 | }); 372 | 373 | group.bench_function("keyed32_8x4", |b| { 374 | b.iter_custom(benched_seek_child::<32, KeyMapping32_8x4>) 375 | }); 376 | 377 | group.bench_function("keyed16_16x1", |b| { 378 | b.iter_custom(benched_seek_child::<16, KeyMapping16_16x1>) 379 | }); 380 | 381 | group.bench_function("keyed16_8x2", |b| { 382 | b.iter_custom(benched_seek_child::<16, KeyMapping16_8x2>) 383 | }); 384 | 385 | group.bench_function("keyed4", |b| { 386 | b.iter_custom(benched_seek_child::<4, KeyMapping4>) 387 | }); 388 | 389 | group.bench_function("sorted_keyed32", |b| { 390 | b.iter_custom(benched_seek_child::<32, SortedKeyedMapping>) 391 | }); 392 | 393 | group.bench_function("sorted_keyed16", |b| { 394 | b.iter_custom(benched_seek_child::<16, SortedKeyedMapping>) 395 | }); 396 | 397 | group.bench_function("sorted_keyed4", |b| { 398 | b.iter_custom(benched_seek_child::<4, SortedKeyedMapping>) 399 | }); 400 | 401 | group.finish(); 402 | } 403 | 404 | criterion_group!(benches, grow_node, add_child, del_child, seek_child); 405 | criterion_main!(benches); 406 | -------------------------------------------------------------------------------- /rart/src/concurrent/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod tree; 2 | -------------------------------------------------------------------------------- /rart/src/concurrent/tree.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /rart/src/iter.rs: -------------------------------------------------------------------------------- 1 | use crate::keys::KeyTrait; 2 | use crate::node::{DefaultNode, Node}; 3 | use crate::partials::Partial; 4 | 5 | type IterEntry<'a, P, V> = (u8, &'a DefaultNode); 6 | type NodeIterator<'a, P, V> = dyn Iterator> + 'a; 7 | 8 | pub struct Iter<'a, K: KeyTrait, P: Partial + 'a, V> { 9 | inner: Box + 'a>, 10 | _marker: std::marker::PhantomData<(K, P)>, 11 | } 12 | 13 | struct IterInner<'a, K: KeyTrait, P: Partial + 'a, V> { 14 | node_iter_stack: Vec<(usize, Box>)>, 15 | 16 | // Pushed and popped with prefix portions as we descend the tree, 17 | cur_key: K, 18 | } 19 | 20 | impl<'a, K: KeyTrait, P: Partial + 'a, V> IterInner<'a, K, P, V> { 21 | pub fn new(node: &'a DefaultNode) -> Self { 22 | let node_iter_stack = vec![( 23 | node.prefix.len(), /* initial tree depth*/ 24 | node.iter(), /* root node iter*/ 25 | )]; 26 | 27 | Self { 28 | node_iter_stack, 29 | cur_key: K::new_from_partial(&node.prefix), 30 | } 31 | } 32 | } 33 | 34 | impl<'a, K: KeyTrait + 'a, P: Partial + 'a, V> Iter<'a, K, P, V> { 35 | pub(crate) fn new(node: Option<&'a DefaultNode>) -> Self { 36 | let Some(root_node) = node else { 37 | return Self { 38 | inner: Box::new(std::iter::empty()), 39 | _marker: Default::default(), 40 | }; 41 | }; 42 | 43 | // If root is a leaf, we can just return it. 44 | if root_node.is_leaf() { 45 | let root_key = K::new_from_partial(&root_node.prefix); 46 | let root_value = root_node 47 | .value() 48 | .expect("corruption: missing data at leaf node during iteration"); 49 | return Self { 50 | inner: Box::new(std::iter::once((root_key, root_value))), 51 | _marker: Default::default(), 52 | }; 53 | } 54 | 55 | Self { 56 | inner: Box::new(IterInner::::new(root_node)), 57 | _marker: Default::default(), 58 | } 59 | } 60 | } 61 | 62 | impl<'a, K: KeyTrait, P: Partial + 'a, V> Iterator for Iter<'a, K, P, V> { 63 | type Item = (K, &'a V); 64 | 65 | fn next(&mut self) -> Option { 66 | self.inner.next() 67 | } 68 | } 69 | 70 | impl<'a, K: KeyTrait, P: Partial + 'a, V> Iterator for IterInner<'a, K, P, V> { 71 | type Item = (K, &'a V); 72 | 73 | fn next(&mut self) -> Option { 74 | loop { 75 | // Get working node iterator off the stack. If there is none, we're done. 76 | let Some((tree_depth, last_iter)) = self.node_iter_stack.last_mut() else { 77 | return None; 78 | }; 79 | let tree_depth = *tree_depth; 80 | 81 | // Pull the next node from the node iterator. If there's none, pop that iterator off 82 | // the stack, truncate our working key length back to the parent's depth, return to our 83 | // parent, and continue there. 84 | let Some((_k, node)) = last_iter.next() else { 85 | let _ = self.node_iter_stack.pop().unwrap(); 86 | // Get the parent-depth, and truncate our working key to that depth. If there is no 87 | // parent, no need to truncate, we'll be done in the next loop 88 | if let Some((parent_depth, _)) = self.node_iter_stack.last() { 89 | self.cur_key = self.cur_key.truncate(*parent_depth); 90 | }; 91 | continue; 92 | }; 93 | 94 | // We're at a non-exhausted inner node, so go further down the tree by pushing node 95 | // iterator into the stack. We also extend our working key with this node's prefix. 96 | if node.is_inner() { 97 | self.node_iter_stack 98 | .push((tree_depth + node.prefix.len(), node.iter())); 99 | self.cur_key = self.cur_key.extend_from_partial(&node.prefix); 100 | continue; 101 | } 102 | 103 | // We've got a value, so tack it onto our working key, and return it. If there's nothing 104 | // here, that's an issue, leaf nodes should always have values. 105 | let v = node 106 | .value() 107 | .expect("corruption: missing data at leaf node during iteration"); 108 | let key = self.cur_key.extend_from_partial(&node.prefix); 109 | return Some((key, v)); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /rart/src/keys/array_key.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | 3 | use crate::keys::KeyTrait; 4 | use crate::partials::array_partial::ArrPartial; 5 | use crate::partials::Partial; 6 | 7 | #[derive(Clone, Copy, Eq, PartialEq)] 8 | pub struct ArrayKey { 9 | data: [u8; N], 10 | len: usize, 11 | } 12 | 13 | impl ArrayKey { 14 | pub fn new_from_str(s: &str) -> Self { 15 | assert!(s.len() + 1 < N, "data length is greater than array length"); 16 | let mut arr = [0; N]; 17 | arr[..s.len()].copy_from_slice(s.as_bytes()); 18 | Self { 19 | data: arr, 20 | len: s.len() + 1, 21 | } 22 | } 23 | 24 | pub fn new_from_string(s: &String) -> Self { 25 | assert!(s.len() + 1 < N, "data length is greater than array length"); 26 | let mut arr = [0; N]; 27 | arr[..s.len()].copy_from_slice(s.as_bytes()); 28 | Self { 29 | data: arr, 30 | len: s.len() + 1, 31 | } 32 | } 33 | 34 | pub fn new_from_array(arr: [u8; S]) -> Self { 35 | Self::new_from_slice(&arr) 36 | } 37 | 38 | pub fn as_array(&self) -> &[u8; N] { 39 | &self.data 40 | } 41 | 42 | pub fn as_slice(&self) -> &[u8] { 43 | &self.data[..self.len] 44 | } 45 | 46 | /// (Convenience function. Not all keys can be assumed to be numeric.) 47 | pub fn to_be_u64(&self) -> u64 { 48 | // Copy from 0..min(len, 8) to a new array left-padding it, then convert to u64. 49 | let mut arr = [0; 8]; 50 | arr[8 - self.len..].copy_from_slice(&self.data[..self.len]); 51 | u64::from_be_bytes(arr) 52 | } 53 | } 54 | 55 | impl KeyTrait for ArrayKey { 56 | type PartialType = ArrPartial; 57 | const MAXIMUM_SIZE: Option = Some(N); 58 | 59 | fn new_from_slice(data: &[u8]) -> Self { 60 | assert!(data.len() <= N, "data length is greater than array length"); 61 | let mut arr = [0; N]; 62 | arr[0..data.len()].copy_from_slice(data); 63 | Self { 64 | data: arr, 65 | len: data.len(), 66 | } 67 | } 68 | 69 | fn new_from_partial(partial: &Self::PartialType) -> Self { 70 | let mut data = [0; N]; 71 | let len = partial.len(); 72 | data[..len].copy_from_slice(&partial.to_slice()[..len]); 73 | Self { data, len } 74 | } 75 | 76 | fn extend_from_partial(&self, partial: &Self::PartialType) -> Self { 77 | let cur_len = self.len; 78 | let partial_len = partial.len(); 79 | assert!( 80 | cur_len + partial_len <= N, 81 | "data length is greater than max key length" 82 | ); 83 | let mut data = [0; N]; 84 | data[..cur_len].copy_from_slice(&self.data[..cur_len]); 85 | let partial_slice = partial.to_slice(); 86 | data[cur_len..cur_len + partial_len].copy_from_slice(&partial_slice[..partial_len]); 87 | Self { 88 | data, 89 | len: cur_len + partial_len, 90 | } 91 | } 92 | 93 | fn truncate(&self, at_depth: usize) -> Self { 94 | assert!(at_depth <= self.len, "truncating beyond key length"); 95 | Self { 96 | data: self.data, 97 | len: at_depth, 98 | } 99 | } 100 | 101 | #[inline(always)] 102 | fn at(&self, pos: usize) -> u8 { 103 | self.data[pos] 104 | } 105 | #[inline(always)] 106 | fn length_at(&self, at_depth: usize) -> usize { 107 | self.len - at_depth 108 | } 109 | fn to_partial(&self, at_depth: usize) -> ArrPartial { 110 | ArrPartial::from_slice(&self.data[at_depth..self.len]) 111 | } 112 | #[inline(always)] 113 | fn matches_slice(&self, slice: &[u8]) -> bool { 114 | &self.data[..self.len] == slice 115 | } 116 | } 117 | 118 | impl From for ArrayKey { 119 | fn from(data: String) -> Self { 120 | Self::new_from_string(&data) 121 | } 122 | } 123 | impl From<&String> for ArrayKey { 124 | fn from(data: &String) -> Self { 125 | Self::new_from_string(data) 126 | } 127 | } 128 | impl From<&str> for ArrayKey { 129 | fn from(data: &str) -> Self { 130 | Self::new_from_str(data) 131 | } 132 | } 133 | macro_rules! impl_from_unsigned { 134 | ( $($t:ty),* ) => { 135 | $( 136 | impl From< $t > for ArrayKey 137 | { 138 | fn from(data: $t) -> Self { 139 | Self::new_from_slice(data.to_be_bytes().as_ref()) 140 | } 141 | } 142 | impl From< &$t > for ArrayKey 143 | { 144 | fn from(data: &$t) -> Self { 145 | Self::new_from_slice(data.to_be_bytes().as_ref()) 146 | } 147 | } 148 | ) * 149 | } 150 | } 151 | impl_from_unsigned!(u8, u16, u32, u64, usize, u128); 152 | 153 | impl From for ArrayKey { 154 | fn from(val: i8) -> Self { 155 | let v: u8 = unsafe { mem::transmute(val) }; 156 | let i = (v ^ 0x80) & 0x80; 157 | let j = i | (v & 0x7F); 158 | let mut data = [0; N]; 159 | data[0] = j; 160 | Self { data, len: 1 } 161 | } 162 | } 163 | 164 | macro_rules! impl_from_signed { 165 | ( $t:ty, $tu:ty ) => { 166 | impl From<$t> for ArrayKey { 167 | fn from(val: $t) -> Self { 168 | let v: $tu = unsafe { mem::transmute(val) }; 169 | let xor = 1 << (std::mem::size_of::<$tu>() - 1); 170 | let i = (v ^ xor) & xor; 171 | let j = i | (v & (<$tu>::MAX >> 1)); 172 | ArrayKey::new_from_slice(j.to_be_bytes().as_ref()) 173 | } 174 | } 175 | 176 | impl From<&$t> for ArrayKey { 177 | fn from(val: &$t) -> Self { 178 | (*val).into() 179 | } 180 | } 181 | }; 182 | } 183 | 184 | impl_from_signed!(i16, u16); 185 | impl_from_signed!(i32, u32); 186 | impl_from_signed!(i64, u64); 187 | impl_from_signed!(i128, u128); 188 | impl_from_signed!(isize, usize); 189 | 190 | #[cfg(test)] 191 | mod test { 192 | use crate::keys::array_key::ArrayKey; 193 | use crate::keys::KeyTrait; 194 | use crate::partials::array_partial::ArrPartial; 195 | 196 | #[test] 197 | fn make_extend_truncate() { 198 | let k = ArrayKey::<8>::new_from_slice(b"hel"); 199 | let p = ArrPartial::<8>::from_slice(b"lo"); 200 | let k2 = k.extend_from_partial(&p); 201 | assert!(k2.matches_slice(b"hello")); 202 | let k3 = k2.truncate(3); 203 | assert!(k3.matches_slice(b"hel")); 204 | } 205 | 206 | #[test] 207 | fn from_to_u64() { 208 | let k: ArrayKey<16> = 123u64.into(); 209 | assert_eq!(k.to_be_u64(), 123u64); 210 | 211 | let k: ArrayKey<16> = 1u64.into(); 212 | assert_eq!(k.to_be_u64(), 1u64); 213 | 214 | let k: ArrayKey<16> = 123213123123123u64.into(); 215 | assert_eq!(k.to_be_u64(), 123213123123123u64); 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /rart/src/keys/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::partials::Partial; 2 | 3 | pub mod array_key; 4 | pub mod vector_key; 5 | 6 | pub trait KeyTrait: Clone + PartialEq + Eq { 7 | type PartialType: Partial + From + Clone + PartialEq; 8 | 9 | const MAXIMUM_SIZE: Option; 10 | 11 | fn new_from_slice(slice: &[u8]) -> Self; 12 | fn new_from_partial(partial: &Self::PartialType) -> Self; 13 | 14 | fn extend_from_partial(&self, partial: &Self::PartialType) -> Self; 15 | fn truncate(&self, at_depth: usize) -> Self; 16 | fn at(&self, pos: usize) -> u8; 17 | fn length_at(&self, at_depth: usize) -> usize; 18 | fn to_partial(&self, at_depth: usize) -> Self::PartialType; 19 | fn matches_slice(&self, slice: &[u8]) -> bool; 20 | } 21 | -------------------------------------------------------------------------------- /rart/src/keys/vector_key.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | 3 | use crate::keys::KeyTrait; 4 | use crate::partials::vector_partial::VectorPartial; 5 | 6 | // Owns variable sized key data. Used especially for strings where a null-termination is required. 7 | #[derive(Clone, Eq, PartialEq)] 8 | pub struct VectorKey { 9 | data: Box<[u8]>, 10 | } 11 | 12 | impl VectorKey { 13 | pub fn new_from_string(s: &String) -> Self { 14 | let mut data = Vec::with_capacity(s.len() + 1); 15 | data.extend_from_slice(s.as_bytes()); 16 | data.push(0); 17 | Self { 18 | data: data.into_boxed_slice(), 19 | } 20 | } 21 | 22 | pub fn new_from_str(s: &str) -> Self { 23 | let mut data = Vec::with_capacity(s.len() + 1); 24 | data.extend_from_slice(s.as_bytes()); 25 | data.push(0); 26 | Self { 27 | data: data.into_boxed_slice(), 28 | } 29 | } 30 | 31 | pub fn new_from_vec(data: Vec) -> Self { 32 | Self { 33 | data: data.into_boxed_slice(), 34 | } 35 | } 36 | 37 | pub fn to_be_u64(&self) -> u64 { 38 | // Value must be at least 8 bytes long. 39 | assert!(self.data.len() >= 8, "data length is less than 8 bytes"); 40 | // Copy from 0..min(len, 8) to a new array left-padding it, then convert to u64. 41 | let mut arr = [0; 8]; 42 | arr[8 - self.data.len()..].copy_from_slice(&self.data[..self.data.len()]); 43 | u64::from_be_bytes(arr) 44 | } 45 | } 46 | 47 | impl KeyTrait for VectorKey { 48 | type PartialType = VectorPartial; 49 | const MAXIMUM_SIZE: Option = None; 50 | 51 | fn extend_from_partial(&self, partial: &Self::PartialType) -> Self { 52 | let mut v = self.data.to_vec(); 53 | v.extend_from_slice(partial.to_slice()); 54 | Self { 55 | data: v.into_boxed_slice(), 56 | } 57 | } 58 | 59 | fn truncate(&self, at_depth: usize) -> Self { 60 | let mut v = self.data.to_vec(); 61 | v.truncate(at_depth); 62 | Self { 63 | data: v.into_boxed_slice(), 64 | } 65 | } 66 | 67 | fn new_from_slice(data: &[u8]) -> Self { 68 | let data = Vec::from(data); 69 | Self { 70 | data: data.into_boxed_slice(), 71 | } 72 | } 73 | fn at(&self, pos: usize) -> u8 { 74 | self.data[pos] 75 | } 76 | 77 | fn length_at(&self, at_depth: usize) -> usize { 78 | self.data.len() - at_depth 79 | } 80 | 81 | fn to_partial(&self, at_depth: usize) -> VectorPartial { 82 | VectorPartial::from_slice(&self.data[at_depth..]) 83 | } 84 | 85 | fn matches_slice(&self, slice: &[u8]) -> bool { 86 | self.data.len() == slice.len() && &self.data[..] == slice 87 | } 88 | 89 | fn new_from_partial(partial: &Self::PartialType) -> Self { 90 | let data = Vec::from(partial.to_slice()); 91 | Self { 92 | data: data.into_boxed_slice(), 93 | } 94 | } 95 | } 96 | 97 | impl From for VectorKey { 98 | fn from(data: String) -> Self { 99 | Self::new_from_string(&data) 100 | } 101 | } 102 | impl From<&String> for VectorKey { 103 | fn from(data: &String) -> Self { 104 | Self::new_from_string(data) 105 | } 106 | } 107 | impl From<&str> for VectorKey { 108 | fn from(data: &str) -> Self { 109 | Self::new_from_str(data) 110 | } 111 | } 112 | macro_rules! impl_from_unsigned { 113 | ( $($t:ty),* ) => { 114 | $( 115 | impl From< $t > for VectorKey 116 | { 117 | fn from(data: $t) -> Self { 118 | VectorKey::new_from_slice(&data.to_be_bytes()) 119 | } 120 | } 121 | impl From< &$t > for VectorKey 122 | { 123 | fn from(data: &$t) -> Self { 124 | (*data).into() 125 | } 126 | } 127 | ) * 128 | } 129 | } 130 | impl_from_unsigned!(u8, u16, u32, u64, usize, u128); 131 | 132 | impl From for VectorKey { 133 | fn from(val: i8) -> Self { 134 | let v: u8 = unsafe { mem::transmute(val) }; 135 | let i = (v ^ 0x80) & 0x80; 136 | let j = i | (v & 0x7F); 137 | let v = vec![j]; 138 | VectorKey::new_from_vec(v) 139 | } 140 | } 141 | 142 | macro_rules! impl_from_signed { 143 | ( $t:ty, $tu:ty ) => { 144 | impl From<$t> for VectorKey { 145 | fn from(val: $t) -> Self { 146 | let v: $tu = unsafe { mem::transmute(val) }; 147 | let xor = 1 << (std::mem::size_of::<$tu>() - 1); 148 | let i = (v ^ xor) & xor; 149 | let j = i | (v & (<$tu>::MAX >> 1)); 150 | VectorKey::new_from_slice(&j.to_be_bytes()) 151 | } 152 | } 153 | 154 | impl From<&$t> for VectorKey { 155 | fn from(val: &$t) -> Self { 156 | (*val).into() 157 | } 158 | } 159 | }; 160 | } 161 | 162 | impl_from_signed!(i16, u16); 163 | impl_from_signed!(i32, u32); 164 | impl_from_signed!(i64, u64); 165 | impl_from_signed!(i128, u128); 166 | impl_from_signed!(isize, usize); 167 | 168 | #[cfg(test)] 169 | mod test { 170 | use crate::keys::vector_key::VectorKey; 171 | use crate::keys::KeyTrait; 172 | use crate::partials::vector_partial::VectorPartial; 173 | 174 | #[test] 175 | fn make_extend_truncate() { 176 | let k = VectorKey::new_from_slice(b"hel"); 177 | let p = VectorPartial::from_slice(b"lo"); 178 | let k2 = k.extend_from_partial(&p); 179 | assert!(k2.matches_slice(b"hello")); 180 | let k3 = k2.truncate(3); 181 | assert!(k3.matches_slice(b"hel")); 182 | } 183 | 184 | #[test] 185 | fn from_to_u64() { 186 | let k: VectorKey = 123u64.into(); 187 | assert_eq!(k.to_be_u64(), 123u64); 188 | 189 | let k: VectorKey = 1u64.into(); 190 | assert_eq!(k.to_be_u64(), 1u64); 191 | 192 | let k: VectorKey = 123213123123123u64.into(); 193 | assert_eq!(k.to_be_u64(), 123213123123123u64); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /rart/src/lib.rs: -------------------------------------------------------------------------------- 1 | use crate::iter::Iter; 2 | use crate::keys::KeyTrait; 3 | use crate::range::Range; 4 | use std::ops::RangeBounds; 5 | 6 | pub mod concurrent; 7 | pub mod iter; 8 | pub mod keys; 9 | pub mod mapping; 10 | mod node; 11 | pub mod partials; 12 | pub mod range; 13 | pub mod stats; 14 | pub mod tree; 15 | pub mod utils; 16 | 17 | pub trait TreeTrait 18 | where 19 | KeyType: KeyTrait, 20 | { 21 | type NodeType; 22 | 23 | fn get(&self, key: Key) -> Option<&ValueType> 24 | where 25 | Key: Into, 26 | { 27 | self.get_k(&key.into()) 28 | } 29 | fn get_k(&self, key: &KeyType) -> Option<&ValueType>; 30 | fn get_mut(&mut self, key: Key) -> Option<&mut ValueType> 31 | where 32 | Key: Into, 33 | { 34 | self.get_mut_k(&key.into()) 35 | } 36 | fn get_mut_k(&mut self, key: &KeyType) -> Option<&mut ValueType>; 37 | fn insert(&mut self, key: KV, value: ValueType) -> Option 38 | where 39 | KV: Into, 40 | { 41 | self.insert_k(&key.into(), value) 42 | } 43 | fn insert_k(&mut self, key: &KeyType, value: ValueType) -> Option; 44 | 45 | fn remove(&mut self, key: KV) -> Option 46 | where 47 | KV: Into, 48 | { 49 | self.remove_k(&key.into()) 50 | } 51 | fn remove_k(&mut self, key: &KeyType) -> Option; 52 | 53 | fn iter(&self) -> Iter; 54 | 55 | fn range<'a, R>(&'a self, range: R) -> Range 56 | where 57 | R: RangeBounds + 'a; 58 | 59 | fn is_empty(&self) -> bool; 60 | } 61 | -------------------------------------------------------------------------------- /rart/src/mapping/direct_mapping.rs: -------------------------------------------------------------------------------- 1 | use crate::mapping::indexed_mapping::IndexedMapping; 2 | use crate::mapping::NodeMapping; 3 | use crate::utils::bitarray::BitArray; 4 | use crate::utils::bitset::{Bitset64, BitsetTrait}; 5 | 6 | pub struct DirectMapping { 7 | pub(crate) children: BitArray>, 8 | num_children: usize, 9 | } 10 | 11 | impl Default for DirectMapping { 12 | fn default() -> Self { 13 | Self::new() 14 | } 15 | } 16 | 17 | impl DirectMapping { 18 | pub fn new() -> Self { 19 | Self { 20 | children: BitArray::new(), 21 | num_children: 0, 22 | } 23 | } 24 | 25 | pub fn from_indexed( 26 | im: &mut IndexedMapping, 27 | ) -> Self { 28 | let mut new_mapping = DirectMapping::::new(); 29 | im.num_children = 0; 30 | im.move_into(&mut new_mapping); 31 | new_mapping 32 | } 33 | 34 | #[inline] 35 | pub fn iter(&self) -> impl Iterator { 36 | self.children.iter().map(|(key, node)| (key as u8, node)) 37 | } 38 | } 39 | 40 | impl NodeMapping for DirectMapping { 41 | #[inline] 42 | fn add_child(&mut self, key: u8, node: N) { 43 | self.children.set(key as usize, node); 44 | self.num_children += 1; 45 | } 46 | 47 | fn update_child(&mut self, key: u8, node: N) { 48 | if let Some(n) = self.children.get_mut(key as usize) { 49 | *n = node; 50 | } 51 | } 52 | 53 | #[inline] 54 | fn seek_child(&self, key: u8) -> Option<&N> { 55 | self.children.get(key as usize) 56 | } 57 | 58 | #[inline] 59 | fn seek_child_mut(&mut self, key: u8) -> Option<&mut N> { 60 | self.children.get_mut(key as usize) 61 | } 62 | 63 | #[inline] 64 | fn delete_child(&mut self, key: u8) -> Option { 65 | let n = self.children.erase(key as usize); 66 | if n.is_some() { 67 | self.num_children -= 1; 68 | } 69 | n 70 | } 71 | 72 | #[inline] 73 | fn num_children(&self) -> usize { 74 | self.num_children 75 | } 76 | } 77 | 78 | #[cfg(test)] 79 | mod tests { 80 | use crate::mapping::NodeMapping; 81 | 82 | #[test] 83 | fn direct_mapping_test() { 84 | let mut dm = super::DirectMapping::new(); 85 | for i in 0..255 { 86 | dm.add_child(i, i); 87 | assert_eq!(*dm.seek_child(i).unwrap(), i); 88 | assert_eq!(dm.delete_child(i), Some(i)); 89 | assert_eq!(dm.seek_child(i), None); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /rart/src/mapping/indexed_mapping.rs: -------------------------------------------------------------------------------- 1 | use std::mem::MaybeUninit; 2 | 3 | use crate::mapping::direct_mapping::DirectMapping; 4 | use crate::mapping::keyed_mapping::KeyedMapping; 5 | use crate::mapping::sorted_keyed_mapping::SortedKeyedMapping; 6 | use crate::mapping::NodeMapping; 7 | use crate::utils::bitarray::BitArray; 8 | use crate::utils::bitset::{Bitset64, BitsetTrait}; 9 | 10 | /// A mapping from keys to separate child pointers. 256 keys, usually 48 children. 11 | pub struct IndexedMapping { 12 | pub(crate) child_ptr_indexes: BitArray>, 13 | pub(crate) children: BitArray, 14 | pub(crate) num_children: u8, 15 | } 16 | 17 | impl Default for IndexedMapping { 18 | fn default() -> Self { 19 | Self::new() 20 | } 21 | } 22 | 23 | impl IndexedMapping { 24 | pub fn new() -> Self { 25 | Self { 26 | child_ptr_indexes: Default::default(), 27 | children: BitArray::new(), 28 | num_children: 0, 29 | } 30 | } 31 | 32 | pub(crate) fn from_direct(dm: &mut DirectMapping) -> Self { 33 | let mut indexed = IndexedMapping::new(); 34 | 35 | let keys: Vec = dm.children.iter_keys().collect(); 36 | for key in keys { 37 | let child = dm.children.erase(key).unwrap(); 38 | indexed.add_child(key as u8, child); 39 | } 40 | indexed 41 | } 42 | 43 | pub fn from_sorted_keyed( 44 | km: &mut SortedKeyedMapping, 45 | ) -> Self { 46 | let mut im: IndexedMapping = IndexedMapping::new(); 47 | for i in 0..km.num_children as usize { 48 | let stolen = std::mem::replace(&mut km.children[i], MaybeUninit::uninit()); 49 | im.add_child(km.keys[i], unsafe { stolen.assume_init() }); 50 | } 51 | km.num_children = 0; 52 | im 53 | } 54 | 55 | pub fn from_keyed( 56 | km: &mut KeyedMapping, 57 | ) -> Self { 58 | let mut im: IndexedMapping = IndexedMapping::new(); 59 | for i in 0..KM_WIDTH { 60 | let Some(stolen) = km.children.erase(i) else { 61 | continue; 62 | }; 63 | im.add_child(km.keys[i], stolen); 64 | } 65 | km.children.clear(); 66 | km.num_children = 0; 67 | im 68 | } 69 | 70 | pub(crate) fn move_into>( 71 | &mut self, 72 | nm: &mut NM, 73 | ) { 74 | for (key, pos) in self.child_ptr_indexes.iter() { 75 | let node = self.children.erase(*pos as usize).unwrap(); 76 | nm.add_child(key as u8, node); 77 | } 78 | } 79 | 80 | pub fn iter(&self) -> impl Iterator { 81 | self.child_ptr_indexes 82 | .iter() 83 | .map(move |(key, pos)| (key as u8, &self.children[*pos as usize])) 84 | } 85 | } 86 | 87 | impl NodeMapping 88 | for IndexedMapping 89 | { 90 | fn add_child(&mut self, key: u8, node: N) { 91 | let pos = self.children.first_empty().unwrap(); 92 | self.child_ptr_indexes.set(key as usize, pos as u8); 93 | self.children.set(pos, node); 94 | self.num_children += 1; 95 | } 96 | 97 | fn update_child(&mut self, key: u8, node: N) { 98 | if let Some(pos) = self.child_ptr_indexes.get(key as usize) { 99 | self.children.set(*pos as usize, node); 100 | } 101 | } 102 | 103 | fn seek_child(&self, key: u8) -> Option<&N> { 104 | if let Some(pos) = self.child_ptr_indexes.get(key as usize) { 105 | return self.children.get(*pos as usize); 106 | } 107 | None 108 | } 109 | 110 | fn seek_child_mut(&mut self, key: u8) -> Option<&mut N> { 111 | if let Some(pos) = self.child_ptr_indexes.get(key as usize) { 112 | return self.children.get_mut(*pos as usize); 113 | } 114 | None 115 | } 116 | 117 | fn delete_child(&mut self, key: u8) -> Option { 118 | let pos = self.child_ptr_indexes.erase(key as usize)?; 119 | 120 | let old = self.children.erase(pos as usize); 121 | self.num_children -= 1; 122 | 123 | // Return what we deleted. 124 | old 125 | } 126 | 127 | fn num_children(&self) -> usize { 128 | self.num_children as usize 129 | } 130 | } 131 | 132 | impl Drop for IndexedMapping { 133 | fn drop(&mut self) { 134 | if self.num_children == 0 { 135 | return; 136 | } 137 | self.num_children = 0; 138 | self.child_ptr_indexes.clear(); 139 | self.children.clear(); 140 | } 141 | } 142 | 143 | #[cfg(test)] 144 | mod test { 145 | use crate::mapping::NodeMapping; 146 | use crate::utils::bitset::Bitset16; 147 | 148 | #[test] 149 | fn test_fits_in_cache_line() { 150 | assert!(std::mem::size_of::>>() <= 64); 151 | } 152 | 153 | #[test] 154 | fn test_basic_mapping() { 155 | let mut mapping = super::IndexedMapping::>::new(); 156 | for i in 0..48 { 157 | mapping.add_child(i, i); 158 | assert_eq!(*mapping.seek_child(i).unwrap(), i); 159 | } 160 | for i in 0..48 { 161 | assert_eq!(*mapping.seek_child(i).unwrap(), i); 162 | } 163 | for i in 0..48 { 164 | assert_eq!(mapping.delete_child(i).unwrap(), i); 165 | } 166 | for i in 0..48 { 167 | assert!(mapping.seek_child(i as u8).is_none()); 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /rart/src/mapping/keyed_mapping.rs: -------------------------------------------------------------------------------- 1 | use crate::mapping::indexed_mapping::IndexedMapping; 2 | use crate::mapping::NodeMapping; 3 | use crate::utils::bitarray::BitArray; 4 | use crate::utils::bitset::BitsetTrait; 5 | use crate::utils::u8_keys::u8_keys_find_key_position; 6 | 7 | /// Maps a key to a node, using an unsorted array of keys and a corresponding array of nodes. 8 | /// Presence of a key at a position means there is a node at the same position in children. 9 | /// A bitmask is used to keep track of which keys are empty. 10 | /// The nodes are kept unsorted, so a linear search is used to find the key, but SIMD operations 11 | /// are used to speed up the search on platforms that have it. 12 | /// Likewise, appends are done by inserting at the first empty slot (by scanning bitset) 13 | pub struct KeyedMapping 14 | where 15 | Bitset: BitsetTrait, 16 | { 17 | pub(crate) keys: [u8; WIDTH], 18 | pub(crate) children: BitArray, 19 | pub(crate) num_children: u8, 20 | } 21 | 22 | impl Default for KeyedMapping 23 | where 24 | Bitset: BitsetTrait, 25 | { 26 | fn default() -> Self { 27 | Self::new() 28 | } 29 | } 30 | 31 | impl KeyedMapping 32 | where 33 | Bitset: BitsetTrait, 34 | { 35 | #[inline] 36 | pub fn new() -> Self { 37 | Self { 38 | keys: [255; WIDTH], 39 | children: Default::default(), 40 | num_children: 0, 41 | } 42 | } 43 | 44 | #[allow(dead_code)] 45 | pub(crate) fn from_indexed( 46 | im: &mut IndexedMapping, 47 | ) -> Self { 48 | let mut new_mapping = KeyedMapping::new(); 49 | im.num_children = 0; 50 | im.move_into(&mut new_mapping); 51 | new_mapping 52 | } 53 | 54 | pub fn from_resized_grow( 55 | km: &mut KeyedMapping, 56 | ) -> Self { 57 | assert!(WIDTH > OLD_WIDTH); 58 | let mut new = KeyedMapping::new(); 59 | 60 | // Since we're larger than before, we can just copy over and expand everything, occupied 61 | // or not. 62 | for i in 0..OLD_WIDTH { 63 | new.keys[i] = km.keys[i]; 64 | let stolen = km.children.erase(i); 65 | if let Some(n) = stolen { 66 | new.children.set(i, n); 67 | } 68 | } 69 | km.children.clear(); 70 | new.num_children = km.num_children; 71 | new 72 | } 73 | 74 | // Return the key and value of the only child, and remove it from the mapping. 75 | pub fn take_value_for_leaf(&mut self) -> (u8, N) { 76 | assert!(self.num_children == 1); 77 | let first_child_pos = self.children.first_used().unwrap(); 78 | let key = self.keys[first_child_pos]; 79 | let value = self.children.erase(first_child_pos).unwrap(); 80 | self.num_children -= 1; 81 | (key, value) 82 | } 83 | 84 | pub fn from_resized_shrink( 85 | km: &mut KeyedMapping, 86 | ) -> Self { 87 | assert!(WIDTH < OLD_WIDTH); 88 | let mut new = KeyedMapping::new(); 89 | let mut cnt = 0; 90 | 91 | // Since we're smaller, we compact empty spots out. 92 | for i in 0..OLD_WIDTH { 93 | if km.children.check(i) { 94 | new.keys[cnt] = km.keys[i]; 95 | let stolen = km.children.erase(i); 96 | if let Some(n) = stolen { 97 | new.children.set(cnt, n); 98 | } 99 | cnt += 1; 100 | } 101 | } 102 | km.children.clear(); 103 | new.num_children = km.num_children; 104 | km.num_children = 0; 105 | new 106 | } 107 | 108 | #[inline] 109 | #[allow(dead_code)] 110 | pub(crate) fn iter(&self) -> impl Iterator { 111 | self.keys 112 | .iter() 113 | .enumerate() 114 | .filter(|p| self.children.check(p.0)) 115 | .map(|p| (*p.1, self.children.get(p.0).unwrap())) 116 | } 117 | } 118 | 119 | impl NodeMapping 120 | for KeyedMapping 121 | { 122 | #[inline] 123 | fn add_child(&mut self, key: u8, node: N) { 124 | // Find an empty position by looking into the bitset. 125 | let idx = self.children.first_empty().unwrap(); 126 | assert!(idx < WIDTH); 127 | self.keys[idx] = key; 128 | self.children.set(idx, node); 129 | self.num_children += 1; 130 | } 131 | 132 | fn update_child(&mut self, key: u8, node: N) { 133 | *self.seek_child_mut(key).unwrap() = node; 134 | } 135 | 136 | fn seek_child(&self, key: u8) -> Option<&N> { 137 | let idx = u8_keys_find_key_position::(key, &self.keys, &self.children.bitset)?; 138 | self.children.get(idx) 139 | } 140 | 141 | fn seek_child_mut(&mut self, key: u8) -> Option<&mut N> { 142 | let idx = u8_keys_find_key_position::(key, &self.keys, &self.children.bitset)?; 143 | self.children.get_mut(idx) 144 | } 145 | 146 | fn delete_child(&mut self, key: u8) -> Option { 147 | // Find position of the key 148 | let idx = u8_keys_find_key_position::(key, &self.keys, &self.children.bitset)?; 149 | let result = self.children.erase(idx); 150 | if result.is_some() { 151 | self.keys[idx] = 255; 152 | self.num_children -= 1; 153 | } 154 | 155 | // Return what we deleted, if any 156 | result 157 | } 158 | 159 | #[inline(always)] 160 | fn num_children(&self) -> usize { 161 | self.num_children as usize 162 | } 163 | } 164 | 165 | impl Drop for KeyedMapping { 166 | fn drop(&mut self) { 167 | self.children.clear(); 168 | self.num_children = 0; 169 | } 170 | } 171 | 172 | #[cfg(test)] 173 | mod tests { 174 | use crate::mapping::keyed_mapping::KeyedMapping; 175 | use crate::mapping::NodeMapping; 176 | use crate::utils::bitset::{Bitset16, Bitset8}; 177 | 178 | #[test] 179 | fn test_fits_in_cache_line() { 180 | assert!(std::mem::size_of::>>() <= 64); 181 | assert!(std::mem::size_of::>>() <= 64); 182 | } 183 | 184 | #[test] 185 | fn test_add_seek_delete() { 186 | let mut node = KeyedMapping::>::new(); 187 | node.add_child(1, 1); 188 | node.add_child(2, 2); 189 | node.add_child(3, 3); 190 | node.add_child(4, 4); 191 | assert_eq!(node.num_children(), 4); 192 | assert_eq!(node.seek_child(1), Some(&1)); 193 | assert_eq!(node.seek_child(2), Some(&2)); 194 | assert_eq!(node.seek_child(3), Some(&3)); 195 | assert_eq!(node.seek_child(4), Some(&4)); 196 | assert_eq!(node.seek_child(5), None); 197 | assert_eq!(node.seek_child_mut(1), Some(&mut 1)); 198 | assert_eq!(node.seek_child_mut(2), Some(&mut 2)); 199 | assert_eq!(node.seek_child_mut(3), Some(&mut 3)); 200 | assert_eq!(node.seek_child_mut(4), Some(&mut 4)); 201 | assert_eq!(node.seek_child_mut(5), None); 202 | assert_eq!(node.delete_child(1), Some(1)); 203 | assert_eq!(node.delete_child(2), Some(2)); 204 | assert_eq!(node.delete_child(3), Some(3)); 205 | assert_eq!(node.delete_child(4), Some(4)); 206 | assert_eq!(node.delete_child(5), None); 207 | assert_eq!(node.num_children(), 0); 208 | } 209 | 210 | #[test] 211 | fn test_ff_regression() { 212 | // Test for scenario where children with '255' keys disappeared. 213 | let mut node = KeyedMapping::>::new(); 214 | node.add_child(1, 1); 215 | node.add_child(2, 255); 216 | node.add_child(3, 3); 217 | node.delete_child(3); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /rart/src/mapping/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod direct_mapping; 2 | pub mod indexed_mapping; 3 | pub mod keyed_mapping; 4 | pub mod sorted_keyed_mapping; 5 | 6 | pub trait NodeMapping { 7 | const NUM_CHILDREN: usize = NUM_CHILDREN; 8 | 9 | fn add_child(&mut self, key: u8, node: N); 10 | fn update_child(&mut self, key: u8, node: N); 11 | fn seek_child(&self, key: u8) -> Option<&N>; 12 | fn seek_child_mut(&mut self, key: u8) -> Option<&mut N>; 13 | fn delete_child(&mut self, key: u8) -> Option; 14 | fn num_children(&self) -> usize; 15 | fn width(&self) -> usize { 16 | Self::NUM_CHILDREN 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rart/src/mapping/sorted_keyed_mapping.rs: -------------------------------------------------------------------------------- 1 | use std::mem::MaybeUninit; 2 | 3 | use crate::mapping::indexed_mapping::IndexedMapping; 4 | use crate::mapping::NodeMapping; 5 | use crate::utils::bitset::BitsetTrait; 6 | use crate::utils::u8_keys::{ 7 | u8_keys_find_insert_position_sorted, u8_keys_find_key_position_sorted, 8 | }; 9 | 10 | /// Maps a key to a node, using a sorted array of keys and a corresponding array of nodes. 11 | /// Presence of a key at a position means there is a node at the same position in children. 12 | /// Empty nodes are represented by 255. 13 | /// By keeping nodes in a sorted array, we can use binary search to find the key, but we also 14 | /// use SIMD instructions to speed up the search on platforms that have it. 15 | /// When an item is inserted or deleted the items to the left and right of it are shifted, in 16 | /// order to keep the array sorted. 17 | /// *Note* this version is currently unused, as it is slower than the unsorted version on x86_64 18 | /// with sse. If benching on other platforms shows it to be faster, then we can use it, so the 19 | /// code is kept here. The bottleneck here is the constant shuffling of the keys in the array. 20 | pub struct SortedKeyedMapping { 21 | pub(crate) keys: [u8; WIDTH], 22 | pub(crate) children: Box<[MaybeUninit; WIDTH]>, 23 | pub(crate) num_children: u8, 24 | } 25 | 26 | impl Default for SortedKeyedMapping { 27 | fn default() -> Self { 28 | Self::new() 29 | } 30 | } 31 | 32 | impl SortedKeyedMapping { 33 | #[inline] 34 | pub fn new() -> Self { 35 | Self { 36 | keys: [255; WIDTH], 37 | children: Box::new(unsafe { MaybeUninit::uninit().assume_init() }), 38 | num_children: 0, 39 | } 40 | } 41 | // Return the key and value of the only child, and remove it from the mapping. 42 | pub fn take_value_for_leaf(&mut self) -> (u8, N) { 43 | assert!(self.num_children == 1); 44 | let value = std::mem::replace(&mut self.children[0], MaybeUninit::uninit()); 45 | let key = self.keys[0]; 46 | self.num_children = 0; 47 | (key, unsafe { value.assume_init() }) 48 | } 49 | 50 | #[allow(dead_code)] 51 | pub(crate) fn from_indexed( 52 | im: &mut IndexedMapping, 53 | ) -> Self { 54 | let mut new_mapping = SortedKeyedMapping::new(); 55 | im.num_children = 0; 56 | im.move_into(&mut new_mapping); 57 | new_mapping 58 | } 59 | 60 | pub fn from_resized(km: &mut SortedKeyedMapping) -> Self { 61 | let mut new = SortedKeyedMapping::new(); 62 | for i in 0..km.num_children as usize { 63 | new.keys[i] = km.keys[i]; 64 | new.children[i] = std::mem::replace(&mut km.children[i], MaybeUninit::uninit()) 65 | } 66 | new.num_children = km.num_children; 67 | km.num_children = 0; 68 | new 69 | } 70 | 71 | #[inline] 72 | #[allow(dead_code)] 73 | pub(crate) fn iter(&self) -> impl Iterator { 74 | self.keys 75 | .iter() 76 | .zip(self.children.iter()) 77 | .take(self.num_children as usize) 78 | .map(|(&k, c)| (k, unsafe { c.assume_init_ref() })) 79 | } 80 | } 81 | 82 | impl NodeMapping for SortedKeyedMapping { 83 | #[inline] 84 | fn add_child(&mut self, key: u8, node: N) { 85 | let idx = u8_keys_find_insert_position_sorted::( 86 | key, 87 | &self.keys, 88 | self.num_children as usize, 89 | ) 90 | .unwrap(); 91 | 92 | for i in (idx..self.num_children as usize).rev() { 93 | self.keys[i + 1] = self.keys[i]; 94 | self.children[i + 1] = std::mem::replace(&mut self.children[i], MaybeUninit::uninit()); 95 | } 96 | self.keys[idx] = key; 97 | self.children[idx].write(node); 98 | self.num_children += 1; 99 | } 100 | 101 | fn update_child(&mut self, key: u8, node: N) { 102 | *self.seek_child_mut(key).unwrap() = node; 103 | } 104 | 105 | fn seek_child(&self, key: u8) -> Option<&N> { 106 | let idx = 107 | u8_keys_find_key_position_sorted::(key, &self.keys, self.num_children as usize)?; 108 | Some(unsafe { self.children[idx].assume_init_ref() }) 109 | } 110 | 111 | fn seek_child_mut(&mut self, key: u8) -> Option<&mut N> { 112 | let idx = 113 | u8_keys_find_key_position_sorted::(key, &self.keys, self.num_children as usize)?; 114 | return Some(unsafe { self.children[idx].assume_init_mut() }); 115 | } 116 | 117 | fn delete_child(&mut self, key: u8) -> Option { 118 | // Find position of the key 119 | let idx = 120 | u8_keys_find_key_position_sorted::(key, &self.keys, self.num_children as usize)?; 121 | 122 | // Remove the value. 123 | let node = std::mem::replace(&mut self.children[idx], MaybeUninit::uninit()); 124 | 125 | // Shift keys and children to the left. 126 | for i in idx..(WIDTH - 1) { 127 | self.keys[i] = self.keys[i + 1]; 128 | self.children[i] = std::mem::replace(&mut self.children[i + 1], MaybeUninit::uninit()); 129 | } 130 | 131 | // Fix the last key and child and adjust count. 132 | self.keys[WIDTH - 1] = 255; 133 | self.children[WIDTH - 1] = MaybeUninit::uninit(); 134 | 135 | self.num_children -= 1; 136 | 137 | // Return what we deleted. 138 | Some(unsafe { node.assume_init() }) 139 | } 140 | #[inline(always)] 141 | fn num_children(&self) -> usize { 142 | self.num_children as usize 143 | } 144 | } 145 | 146 | impl Drop for SortedKeyedMapping { 147 | fn drop(&mut self) { 148 | for value in &mut self.children[..self.num_children as usize] { 149 | unsafe { value.assume_init_drop() } 150 | } 151 | self.num_children = 0; 152 | } 153 | } 154 | 155 | #[cfg(test)] 156 | mod tests { 157 | use crate::mapping::sorted_keyed_mapping::{NodeMapping, SortedKeyedMapping}; 158 | 159 | #[test] 160 | fn test_add_seek_delete() { 161 | let mut node = SortedKeyedMapping::::new(); 162 | node.add_child(1, 1); 163 | node.add_child(2, 2); 164 | node.add_child(3, 3); 165 | node.add_child(4, 4); 166 | assert_eq!(node.num_children(), 4); 167 | assert_eq!(node.seek_child(1), Some(&1)); 168 | assert_eq!(node.seek_child(2), Some(&2)); 169 | assert_eq!(node.seek_child(3), Some(&3)); 170 | assert_eq!(node.seek_child(4), Some(&4)); 171 | assert_eq!(node.seek_child(5), None); 172 | assert_eq!(node.seek_child_mut(1), Some(&mut 1)); 173 | assert_eq!(node.seek_child_mut(2), Some(&mut 2)); 174 | assert_eq!(node.seek_child_mut(3), Some(&mut 3)); 175 | assert_eq!(node.seek_child_mut(4), Some(&mut 4)); 176 | assert_eq!(node.seek_child_mut(5), None); 177 | assert_eq!(node.delete_child(1), Some(1)); 178 | assert_eq!(node.delete_child(2), Some(2)); 179 | assert_eq!(node.delete_child(3), Some(3)); 180 | assert_eq!(node.delete_child(4), Some(4)); 181 | assert_eq!(node.delete_child(5), None); 182 | assert_eq!(node.num_children(), 0); 183 | } 184 | 185 | #[test] 186 | // Verify that the memory width of the node is nice and compact. 187 | fn test_memory_width() { 188 | // 16 is padded width for 4 children 189 | // num_children = 1 190 | // keys = 4 191 | // children array ptr = 8 192 | // total = 13 pads out to 16 193 | assert_eq!(std::mem::size_of::, 4>>(), 16); 194 | 195 | // 32 is the padded size of the struct on account of 196 | // num_children + keys (u8) + children ptrs 197 | assert_eq!(std::mem::size_of::, 16>>(), 32); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /rart/src/node.rs: -------------------------------------------------------------------------------- 1 | use crate::mapping::direct_mapping::DirectMapping; 2 | use crate::mapping::indexed_mapping::IndexedMapping; 3 | 4 | use crate::mapping::sorted_keyed_mapping::SortedKeyedMapping; 5 | use crate::mapping::NodeMapping; 6 | use crate::partials::Partial; 7 | use crate::utils::bitset::Bitset64; 8 | 9 | pub trait Node { 10 | fn new_leaf(partial: P, value: V) -> Self; 11 | fn new_inner(prefix: P) -> Self; 12 | 13 | fn value(&self) -> Option<&V>; 14 | fn value_mut(&mut self) -> Option<&mut V>; 15 | 16 | fn is_leaf(&self) -> bool; 17 | fn is_inner(&self) -> bool; 18 | 19 | fn seek_child(&self, key: u8) -> Option<&Self>; 20 | fn seek_child_mut(&mut self, key: u8) -> Option<&mut Self>; 21 | 22 | fn add_child(&mut self, key: u8, node: Self); 23 | 24 | fn delete_child(&mut self, key: u8) -> Option 25 | where 26 | Self: Sized; 27 | 28 | fn capacity(&self) -> usize; 29 | fn num_children(&self) -> usize; 30 | } 31 | 32 | pub struct DefaultNode { 33 | pub(crate) prefix: P, 34 | pub(crate) content: Content, 35 | } 36 | 37 | pub(crate) enum Content { 38 | Leaf(V), 39 | Node4(SortedKeyedMapping, 4>), 40 | Node16(SortedKeyedMapping, 16>), 41 | Node48(IndexedMapping, 48, Bitset64<1>>), 42 | Node256(DirectMapping>), 43 | } 44 | 45 | impl Node for DefaultNode { 46 | #[inline] 47 | fn new_leaf(partial: P, value: V) -> Self { 48 | Self { 49 | prefix: partial, 50 | content: Content::Leaf(value), 51 | } 52 | } 53 | 54 | #[inline] 55 | fn new_inner(prefix: P) -> Self { 56 | let nt = Content::Node4(SortedKeyedMapping::new()); 57 | Self { 58 | prefix, 59 | content: nt, 60 | } 61 | } 62 | 63 | fn value(&self) -> Option<&V> { 64 | let Content::Leaf(value) = &self.content else { 65 | return None; 66 | }; 67 | Some(value) 68 | } 69 | 70 | #[allow(dead_code)] 71 | fn value_mut(&mut self) -> Option<&mut V> { 72 | let Content::Leaf(value) = &mut self.content else { 73 | return None; 74 | }; 75 | Some(value) 76 | } 77 | 78 | fn is_leaf(&self) -> bool { 79 | matches!(&self.content, Content::Leaf(_)) 80 | } 81 | 82 | fn is_inner(&self) -> bool { 83 | !self.is_leaf() 84 | } 85 | 86 | fn seek_child(&self, key: u8) -> Option<&Self> { 87 | if self.num_children() == 0 { 88 | return None; 89 | } 90 | 91 | match &self.content { 92 | Content::Node4(km) => km.seek_child(key), 93 | Content::Node16(km) => km.seek_child(key), 94 | Content::Node48(km) => km.seek_child(key), 95 | Content::Node256(children) => children.seek_child(key), 96 | Content::Leaf(_) => None, 97 | } 98 | } 99 | fn seek_child_mut(&mut self, key: u8) -> Option<&mut Self> { 100 | match &mut self.content { 101 | Content::Node4(km) => km.seek_child_mut(key), 102 | Content::Node16(km) => km.seek_child_mut(key), 103 | Content::Node48(km) => km.seek_child_mut(key), 104 | Content::Node256(children) => children.seek_child_mut(key), 105 | Content::Leaf(_) => None, 106 | } 107 | } 108 | 109 | fn add_child(&mut self, key: u8, node: Self) { 110 | if self.is_full() { 111 | self.grow(); 112 | } 113 | 114 | match &mut self.content { 115 | Content::Node4(km) => { 116 | km.add_child(key, node); 117 | } 118 | Content::Node16(km) => { 119 | km.add_child(key, node); 120 | } 121 | Content::Node48(im) => { 122 | im.add_child(key, node); 123 | } 124 | Content::Node256(pm) => { 125 | pm.add_child(key, node); 126 | } 127 | Content::Leaf(_) => unreachable!("Should not be possible."), 128 | } 129 | } 130 | 131 | fn delete_child(&mut self, key: u8) -> Option { 132 | match &mut self.content { 133 | Content::Node4(dm) => { 134 | let node = dm.delete_child(key); 135 | 136 | if self.num_children() == 1 { 137 | self.shrink(); 138 | } 139 | 140 | node 141 | } 142 | Content::Node16(dm) => { 143 | let node = dm.delete_child(key); 144 | 145 | if self.num_children() < 5 { 146 | self.shrink(); 147 | } 148 | node 149 | } 150 | Content::Node48(im) => { 151 | let node = im.delete_child(key); 152 | 153 | if self.num_children() < 17 { 154 | self.shrink(); 155 | } 156 | 157 | // Return what we deleted. 158 | node 159 | } 160 | Content::Node256(pm) => { 161 | let node = pm.delete_child(key); 162 | if self.num_children() < 49 { 163 | self.shrink(); 164 | } 165 | 166 | // Return what we deleted. 167 | node 168 | } 169 | Content::Leaf(_) => unreachable!("Should not be possible."), 170 | } 171 | } 172 | 173 | fn capacity(&self) -> usize { 174 | match &self.content { 175 | Content::Node4 { .. } => 4, 176 | Content::Node16 { .. } => 16, 177 | Content::Node48 { .. } => 48, 178 | Content::Node256 { .. } => 256, 179 | Content::Leaf(_) => 0, 180 | } 181 | } 182 | 183 | fn num_children(&self) -> usize { 184 | match &self.content { 185 | Content::Node4(n) => n.num_children(), 186 | Content::Node16(n) => n.num_children(), 187 | Content::Node48(n) => n.num_children(), 188 | Content::Node256(n) => n.num_children(), 189 | Content::Leaf(_) => 0, 190 | } 191 | } 192 | } 193 | 194 | impl DefaultNode { 195 | #[inline] 196 | #[allow(dead_code)] 197 | pub fn new_4(prefix: P) -> Self { 198 | let nt = Content::Node4(SortedKeyedMapping::new()); 199 | Self { 200 | prefix, 201 | content: nt, 202 | } 203 | } 204 | 205 | #[inline] 206 | #[allow(dead_code)] 207 | pub fn new_16(prefix: P) -> Self { 208 | let nt = Content::Node16(SortedKeyedMapping::new()); 209 | Self { 210 | prefix, 211 | content: nt, 212 | } 213 | } 214 | 215 | #[inline] 216 | #[allow(dead_code)] 217 | pub fn new_48(prefix: P) -> Self { 218 | let nt = Content::Node48(IndexedMapping::new()); 219 | Self { 220 | prefix, 221 | content: nt, 222 | } 223 | } 224 | 225 | #[inline] 226 | #[allow(dead_code)] 227 | pub fn new_256(prefix: P) -> Self { 228 | let nt = Content::Node256(DirectMapping::new()); 229 | Self { 230 | prefix, 231 | content: nt, 232 | } 233 | } 234 | 235 | #[inline] 236 | fn is_full(&self) -> bool { 237 | match &self.content { 238 | Content::Node4(km) => self.num_children() >= km.width(), 239 | Content::Node16(km) => self.num_children() >= km.width(), 240 | Content::Node48(im) => self.num_children() >= im.width(), 241 | // Should not be possible. 242 | Content::Node256(_) => self.num_children() >= 256, 243 | Content::Leaf(_) => unreachable!("Should not be possible."), 244 | } 245 | } 246 | 247 | fn shrink(&mut self) { 248 | match &mut self.content { 249 | Content::Node4(km) => { 250 | // A node4 with only one child has its childed collapsed into it. 251 | // If our child is a leaf, that means we have become a leaf, and we can shrink no 252 | // more beyond this. 253 | let (_, child) = km.take_value_for_leaf(); 254 | let prefix = child.prefix; 255 | self.content = child.content; 256 | self.prefix = self.prefix.partial_extended_with(&prefix); 257 | } 258 | Content::Node16(km) => { 259 | self.content = Content::Node4(SortedKeyedMapping::from_resized(km)); 260 | } 261 | Content::Node48(im) => { 262 | let new_node = Content::Node16(SortedKeyedMapping::from_indexed(im)); 263 | self.content = new_node; 264 | } 265 | Content::Node256(dm) => { 266 | self.content = Content::Node48(IndexedMapping::from_direct(dm)); 267 | } 268 | Content::Leaf(_) => unreachable!("Should not be possible."), 269 | } 270 | } 271 | 272 | fn grow(&mut self) { 273 | match &mut self.content { 274 | Content::Node4(km) => { 275 | self.content = Content::Node16(SortedKeyedMapping::from_resized(km)) 276 | } 277 | Content::Node16(km) => { 278 | self.content = Content::Node48(IndexedMapping::from_sorted_keyed(km)) 279 | } 280 | Content::Node48(im) => { 281 | self.content = Content::Node256(DirectMapping::from_indexed(im)); 282 | } 283 | Content::Node256 { .. } => { 284 | unreachable!("Should never grow a node256") 285 | } 286 | Content::Leaf(_) => unreachable!("Should not be possible."), 287 | } 288 | } 289 | 290 | #[allow(dead_code)] 291 | pub(crate) fn free(&self) -> usize { 292 | self.capacity() - self.num_children() 293 | } 294 | 295 | pub fn iter(&self) -> Box + '_> { 296 | return match &self.content { 297 | Content::Node4(n) => Box::new(n.iter()), 298 | Content::Node16(n) => Box::new(n.iter()), 299 | Content::Node48(n) => Box::new(n.iter()), 300 | Content::Node256(n) => Box::new(n.iter().map(|(k, v)| (k, v))), 301 | Content::Leaf(_) => Box::new(std::iter::empty()), 302 | }; 303 | } 304 | } 305 | 306 | #[cfg(test)] 307 | mod tests { 308 | use crate::node::{DefaultNode, Node}; 309 | use crate::partials::array_partial::ArrPartial; 310 | 311 | #[test] 312 | fn test_n4() { 313 | let test_key: ArrPartial<16> = ArrPartial::key("abc".as_bytes()); 314 | 315 | let mut n4 = DefaultNode::new_4(test_key.clone()); 316 | n4.add_child(5, DefaultNode::new_leaf(test_key.clone(), 1)); 317 | n4.add_child(4, DefaultNode::new_leaf(test_key.clone(), 2)); 318 | n4.add_child(3, DefaultNode::new_leaf(test_key.clone(), 3)); 319 | n4.add_child(2, DefaultNode::new_leaf(test_key.clone(), 4)); 320 | 321 | assert_eq!(*n4.seek_child(5).unwrap().value().unwrap(), 1); 322 | assert_eq!(*n4.seek_child(4).unwrap().value().unwrap(), 2); 323 | assert_eq!(*n4.seek_child(3).unwrap().value().unwrap(), 3); 324 | assert_eq!(*n4.seek_child(2).unwrap().value().unwrap(), 4); 325 | 326 | n4.delete_child(5); 327 | assert!(n4.seek_child(5).is_none()); 328 | assert_eq!(*n4.seek_child(4).unwrap().value().unwrap(), 2); 329 | assert_eq!(*n4.seek_child(3).unwrap().value().unwrap(), 3); 330 | assert_eq!(*n4.seek_child(2).unwrap().value().unwrap(), 4); 331 | 332 | n4.delete_child(2); 333 | assert!(n4.seek_child(5).is_none()); 334 | assert!(n4.seek_child(2).is_none()); 335 | 336 | n4.add_child(2, DefaultNode::new_leaf(test_key, 4)); 337 | n4.delete_child(3); 338 | assert!(n4.seek_child(5).is_none()); 339 | assert!(n4.seek_child(3).is_none()); 340 | } 341 | 342 | #[test] 343 | fn test_n16() { 344 | let test_key: ArrPartial<16> = ArrPartial::key("abc".as_bytes()); 345 | 346 | let mut n16 = DefaultNode::new_16(test_key.clone()); 347 | 348 | // Fill up the node with keys in reverse order. 349 | for i in (0..16).rev() { 350 | n16.add_child(i, DefaultNode::new_leaf(test_key.clone(), i)); 351 | } 352 | 353 | for i in 0..16 { 354 | assert_eq!(*n16.seek_child(i).unwrap().value().unwrap(), i); 355 | } 356 | 357 | // Delete from end doesn't affect position of others. 358 | n16.delete_child(15); 359 | n16.delete_child(14); 360 | assert!(n16.seek_child(15).is_none()); 361 | assert!(n16.seek_child(14).is_none()); 362 | for i in 0..14 { 363 | assert_eq!(*n16.seek_child(i).unwrap().value().unwrap(), i); 364 | } 365 | 366 | n16.delete_child(0); 367 | n16.delete_child(1); 368 | assert!(n16.seek_child(0).is_none()); 369 | assert!(n16.seek_child(1).is_none()); 370 | for i in 2..14 { 371 | assert_eq!(*n16.seek_child(i).unwrap().value().unwrap(), i); 372 | } 373 | 374 | // Delete from the middle 375 | n16.delete_child(5); 376 | n16.delete_child(6); 377 | assert!(n16.seek_child(5).is_none()); 378 | assert!(n16.seek_child(6).is_none()); 379 | for i in 2..5 { 380 | assert_eq!(*n16.seek_child(i).unwrap().value().unwrap(), i); 381 | } 382 | for i in 7..14 { 383 | assert_eq!(*n16.seek_child(i).unwrap().value().unwrap(), i); 384 | } 385 | } 386 | 387 | #[test] 388 | fn test_n48() { 389 | let test_key: ArrPartial<16> = ArrPartial::key("abc".as_bytes()); 390 | 391 | let mut n48 = DefaultNode::new_48(test_key.clone()); 392 | 393 | // indexes in n48 have no sort order, so we don't look at that 394 | for i in 0..48 { 395 | n48.add_child(i, DefaultNode::new_leaf(test_key.clone(), i)); 396 | } 397 | 398 | for i in 0..48 { 399 | assert_eq!(*n48.seek_child(i).unwrap().value().unwrap(), i); 400 | } 401 | 402 | n48.delete_child(47); 403 | n48.delete_child(46); 404 | assert!(n48.seek_child(47).is_none()); 405 | assert!(n48.seek_child(46).is_none()); 406 | for i in 0..46 { 407 | assert_eq!(*n48.seek_child(i).unwrap().value().unwrap(), i); 408 | } 409 | } 410 | 411 | #[test] 412 | fn test_n_256() { 413 | let test_key: ArrPartial<16> = ArrPartial::key("abc".as_bytes()); 414 | 415 | let mut n256 = DefaultNode::new_256(test_key.clone()); 416 | 417 | for i in 0..=255 { 418 | n256.add_child(i, DefaultNode::new_leaf(test_key.clone(), i)); 419 | } 420 | for i in 0..=255 { 421 | assert_eq!(*n256.seek_child(i).unwrap().value().unwrap(), i); 422 | } 423 | 424 | n256.delete_child(47); 425 | n256.delete_child(46); 426 | assert!(n256.seek_child(47).is_none()); 427 | assert!(n256.seek_child(46).is_none()); 428 | for i in 0..46 { 429 | assert_eq!(*n256.seek_child(i).unwrap().value().unwrap(), i); 430 | } 431 | for i in 48..=255 { 432 | assert_eq!(*n256.seek_child(i).unwrap().value().unwrap(), i); 433 | } 434 | } 435 | } 436 | -------------------------------------------------------------------------------- /rart/src/partials/array_partial.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::min; 2 | use std::ops::Index; 3 | 4 | use crate::keys::array_key::ArrayKey; 5 | use crate::keys::KeyTrait; 6 | use crate::partials::Partial; 7 | 8 | #[derive(Clone, Debug, Eq)] 9 | pub struct ArrPartial { 10 | data: [u8; SIZE], 11 | len: usize, 12 | } 13 | 14 | impl PartialEq for ArrPartial { 15 | fn eq(&self, other: &Self) -> bool { 16 | self.data[..self.len] == other.data[..other.len] 17 | } 18 | } 19 | impl ArrPartial { 20 | pub fn key(src: &[u8]) -> Self { 21 | assert!(src.len() < SIZE); 22 | let mut data = [0; SIZE]; 23 | data[..src.len()].copy_from_slice(src); 24 | Self { 25 | data, 26 | len: src.len() + 1, 27 | } 28 | } 29 | 30 | pub fn from_slice(src: &[u8]) -> Self { 31 | assert!( 32 | src.len() <= SIZE, 33 | "data length {} is greater than maximum partial length {}", 34 | src.len(), 35 | SIZE 36 | ); 37 | let mut data = [0; SIZE]; 38 | data[..src.len()].copy_from_slice(src); 39 | Self { 40 | data, 41 | len: src.len(), 42 | } 43 | } 44 | 45 | pub fn to_slice(&self) -> &[u8] { 46 | &self.data[..self.len] 47 | } 48 | } 49 | 50 | impl Index for ArrPartial { 51 | type Output = u8; 52 | 53 | fn index(&self, index: usize) -> &Self::Output { 54 | self.data.index(index) 55 | } 56 | } 57 | impl Partial for ArrPartial { 58 | fn partial_before(&self, length: usize) -> Self { 59 | assert!(length <= self.len); 60 | ArrPartial::from_slice(&self.data[..length]) 61 | } 62 | 63 | fn partial_from(&self, src_offset: usize, length: usize) -> Self { 64 | assert!(src_offset + length <= self.len); 65 | ArrPartial::from_slice(&self.data[src_offset..src_offset + length]) 66 | } 67 | 68 | fn partial_after(&self, start: usize) -> Self { 69 | assert!(start <= self.len); 70 | ArrPartial::from_slice(&self.data[start..self.len]) 71 | } 72 | 73 | fn partial_extended_with(&self, other: &Self) -> Self { 74 | assert!(self.len + other.len < SIZE); 75 | let mut data = [0; SIZE]; 76 | data[..self.len].copy_from_slice(&self.data[..self.len]); 77 | data[self.len..self.len + other.len].copy_from_slice(&other.data[..other.len]); 78 | Self { 79 | data, 80 | len: self.len + other.len, 81 | } 82 | } 83 | 84 | #[inline(always)] 85 | fn at(&self, pos: usize) -> u8 { 86 | assert!(pos < self.len); 87 | self.data[pos] 88 | } 89 | 90 | #[inline(always)] 91 | fn len(&self) -> usize { 92 | self.len 93 | } 94 | 95 | fn prefix_length_common(&self, other: &Self) -> usize { 96 | self.prefix_length_slice(other.to_slice()) 97 | } 98 | 99 | fn prefix_length_key<'a, K>(&self, key: &'a K, at_depth: usize) -> usize 100 | where 101 | K: KeyTrait + 'a, 102 | { 103 | let len = min(self.len, key.length_at(0)); 104 | let len = min(len, SIZE); 105 | let mut idx = 0; 106 | while idx < len { 107 | if self.data[idx] != key.at(idx + at_depth) { 108 | break; 109 | } 110 | idx += 1; 111 | } 112 | idx 113 | } 114 | 115 | fn prefix_length_slice(&self, slice: &[u8]) -> usize { 116 | let len = min(self.len, slice.len()); 117 | let len = min(len, SIZE); 118 | let mut idx = 0; 119 | while idx < len { 120 | if self.data[idx] != slice[idx] { 121 | break; 122 | } 123 | idx += 1; 124 | } 125 | idx 126 | } 127 | 128 | fn to_slice(&self) -> &[u8] { 129 | &self.data[..self.len] 130 | } 131 | } 132 | 133 | impl From<&[u8]> for ArrPartial { 134 | fn from(src: &[u8]) -> Self { 135 | Self::from_slice(src) 136 | } 137 | } 138 | 139 | impl From> for ArrPartial { 140 | fn from(value: ArrayKey) -> Self { 141 | value.to_partial(0) 142 | } 143 | } 144 | 145 | #[cfg(test)] 146 | mod tests { 147 | use super::*; 148 | 149 | #[test] 150 | fn test_partial_before() { 151 | let arr: ArrPartial<16> = ArrPartial::from_slice(b"Hello, world!"); 152 | assert_eq!(arr.partial_before(5).to_slice(), b"Hello"); 153 | } 154 | 155 | #[test] 156 | fn test_partial_from() { 157 | let arr: ArrPartial<16> = ArrPartial::from_slice(b"Hello, world!"); 158 | assert_eq!(arr.partial_from(7, 5).to_slice(), b"world"); 159 | } 160 | 161 | #[test] 162 | fn test_prefix_after() { 163 | let arr: ArrPartial<16> = ArrPartial::from_slice(b"Hello, world!"); 164 | assert_eq!(arr.partial_after(7).to_slice(), b"world!"); 165 | } 166 | 167 | #[test] 168 | fn test_at() { 169 | let arr: ArrPartial<16> = ArrPartial::from_slice(b"Hello, world!"); 170 | assert_eq!(arr.at(0), 72); 171 | } 172 | 173 | #[test] 174 | fn test_length() { 175 | let arr: ArrPartial<16> = ArrPartial::from_slice(b"Hello, world!"); 176 | assert_eq!(arr.len(), 13); 177 | } 178 | 179 | #[test] 180 | fn test_prefix_length_common() { 181 | let arr1: ArrPartial<16> = ArrPartial::from_slice(b"Hello, world!"); 182 | let arr2: ArrPartial<16> = ArrPartial::from_slice(b"Hello, there!"); 183 | assert_eq!(arr1.prefix_length_common(&arr2), 7); 184 | } 185 | 186 | #[test] 187 | fn test_key() { 188 | let arr: ArrPartial<16> = ArrPartial::key(b"Hello, world!"); 189 | assert_eq!( 190 | arr.to_slice(), 191 | &[72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33, 0] 192 | ); 193 | } 194 | 195 | #[test] 196 | fn test_from_slice() { 197 | let arr: ArrPartial<16> = ArrPartial::from_slice(b"Hello, world!"); 198 | assert_eq!(arr.to_slice(), b"Hello, world!"); 199 | } 200 | 201 | #[test] 202 | fn test_partial_chain_with_key() { 203 | let arr1: ArrPartial<16> = ArrPartial::key(b"Hello, world!"); 204 | let arr2: ArrPartial<16> = ArrPartial::key(b"Hello, there!"); 205 | let partial1 = arr1.partial_before(6); 206 | assert_eq!(partial1.to_slice(), b"Hello,"); 207 | let partial2 = arr2.partial_from(7, 5); 208 | assert_eq!(partial2.to_slice(), b"there"); 209 | let partial3 = partial1.partial_after(1); 210 | assert_eq!(partial3.to_slice(), b"ello,"); 211 | assert_eq!(0, partial3.prefix_length_common(&partial2)); 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /rart/src/partials/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::keys::KeyTrait; 2 | 3 | pub mod array_partial; 4 | pub mod vector_partial; 5 | 6 | pub trait Partial { 7 | /// Returns a partial up to `length` bytes. 8 | fn partial_before(&self, length: usize) -> Self; 9 | /// Returns a partial from `src_offset` onwards with `length` bytes. 10 | fn partial_from(&self, src_offset: usize, length: usize) -> Self; 11 | /// Returns a partial from `start` onwards. 12 | fn partial_after(&self, start: usize) -> Self; 13 | /// Extends the partial with another partial. 14 | fn partial_extended_with(&self, other: &Self) -> Self; 15 | /// Returns the byte at `pos`. 16 | fn at(&self, pos: usize) -> u8; 17 | /// Returns the length of the partial. 18 | fn len(&self) -> usize; 19 | /// Returns true if the partial is empty. 20 | fn is_empty(&self) -> bool { 21 | self.len() == 0 22 | } 23 | /// Returns the length of the common prefix between `self` and `other`. 24 | fn prefix_length_common(&self, other: &Self) -> usize; 25 | /// Returns the length of the common prefix between `self` and `key`. 26 | fn prefix_length_key<'a, K>(&self, key: &'a K, at_depth: usize) -> usize 27 | where 28 | K: KeyTrait + 'a; 29 | /// Returns the length of the common prefix between `self` and `slice`. 30 | fn prefix_length_slice(&self, slice: &[u8]) -> usize; 31 | /// Return a slice form of the partial. Warning: could take copy, depending on the implementation. 32 | /// Really just for debugging purposes. 33 | fn to_slice(&self) -> &[u8]; 34 | } 35 | -------------------------------------------------------------------------------- /rart/src/partials/vector_partial.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::min; 2 | 3 | use crate::keys::vector_key::VectorKey; 4 | use crate::keys::KeyTrait; 5 | use crate::partials::Partial; 6 | 7 | #[derive(Clone, PartialEq, Eq, Debug)] 8 | pub struct VectorPartial { 9 | data: Box<[u8]>, 10 | } 11 | 12 | impl VectorPartial { 13 | pub fn key(src: &[u8]) -> Self { 14 | let mut data = Vec::with_capacity(src.len() + 1); 15 | data.extend_from_slice(src); 16 | data.push(0); 17 | Self { 18 | data: data.into_boxed_slice(), 19 | } 20 | } 21 | 22 | pub fn from_slice(src: &[u8]) -> Self { 23 | Self { 24 | data: Box::from(src), 25 | } 26 | } 27 | 28 | pub fn to_slice(&self) -> &[u8] { 29 | &self.data 30 | } 31 | } 32 | 33 | impl From<&[u8]> for VectorPartial { 34 | fn from(src: &[u8]) -> Self { 35 | Self::from_slice(src) 36 | } 37 | } 38 | 39 | impl Partial for VectorPartial { 40 | fn partial_before(&self, length: usize) -> Self { 41 | assert!(length <= self.data.len()); 42 | VectorPartial::from_slice(&self.data[..length]) 43 | } 44 | 45 | fn partial_from(&self, src_offset: usize, length: usize) -> Self { 46 | assert!(src_offset + length <= self.data.len()); 47 | VectorPartial::from_slice(&self.data[src_offset..src_offset + length]) 48 | } 49 | 50 | fn partial_after(&self, start: usize) -> Self { 51 | assert!(start <= self.data.len()); 52 | VectorPartial::from_slice(&self.data[start..self.data.len()]) 53 | } 54 | 55 | fn partial_extended_with(&self, other: &Self) -> Self { 56 | let mut data = Vec::with_capacity(self.data.len() + other.data.len()); 57 | data.extend_from_slice(&self.data); 58 | data.extend_from_slice(&other.data); 59 | Self { 60 | data: data.into_boxed_slice(), 61 | } 62 | } 63 | 64 | #[inline(always)] 65 | fn at(&self, pos: usize) -> u8 { 66 | assert!(pos < self.data.len()); 67 | self.data[pos] 68 | } 69 | 70 | #[inline(always)] 71 | fn len(&self) -> usize { 72 | self.data.len() 73 | } 74 | 75 | fn prefix_length_common(&self, other: &Self) -> usize { 76 | self.prefix_length_slice(other.to_slice()) 77 | } 78 | 79 | fn prefix_length_key<'a, K>(&self, key: &'a K, at_depth: usize) -> usize 80 | where 81 | K: KeyTrait + 'a, 82 | { 83 | let len = min(self.data.len(), key.length_at(0)); 84 | let mut idx = 0; 85 | while idx < len { 86 | if self.data[idx] != key.at(idx + at_depth) { 87 | break; 88 | } 89 | idx += 1; 90 | } 91 | idx 92 | } 93 | 94 | fn prefix_length_slice(&self, slice: &[u8]) -> usize { 95 | let len = min(self.data.len(), slice.len()); 96 | let mut idx = 0; 97 | while idx < len { 98 | if self.data[idx] != slice[idx] { 99 | break; 100 | } 101 | idx += 1; 102 | } 103 | idx 104 | } 105 | 106 | fn to_slice(&self) -> &[u8] { 107 | &self.data[..self.data.len()] 108 | } 109 | } 110 | 111 | impl From for VectorPartial { 112 | fn from(value: VectorKey) -> Self { 113 | value.to_partial(0) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /rart/src/range.rs: -------------------------------------------------------------------------------- 1 | use std::collections::Bound; 2 | 3 | use crate::iter::Iter; 4 | use crate::keys::KeyTrait; 5 | use crate::partials::Partial; 6 | 7 | enum InnerResult<'a, K, V> { 8 | OneMore((K, &'a V)), 9 | Iter(Option<(K, &'a V)>), 10 | } 11 | 12 | struct RangeInner<'a, K: KeyTrait + 'a, V> { 13 | iter: Iter<'a, K, K::PartialType, V>, 14 | end: Bound, 15 | } 16 | 17 | struct RangeInnerNone {} 18 | 19 | trait RangeInnerTrait<'a, K: KeyTrait + 'a, V> { 20 | fn next(&mut self) -> InnerResult<'a, K, V>; 21 | } 22 | 23 | pub struct Range<'a, K: KeyTrait + 'a, V> { 24 | inner: Box + 'a>, 25 | } 26 | 27 | impl<'a, K: KeyTrait + 'a, V> RangeInnerTrait<'a, K, V> for RangeInnerNone { 28 | fn next(&mut self) -> InnerResult<'a, K, V> { 29 | InnerResult::Iter(None) 30 | } 31 | } 32 | 33 | impl<'a, K: KeyTrait, P: Partial, V> RangeInner<'a, K, V> { 34 | pub fn new(iter: Iter<'a, K, P, V>, end: Bound) -> Self { 35 | Self { iter, end } 36 | } 37 | } 38 | 39 | impl<'a, K: KeyTrait + 'a, V> RangeInnerTrait<'a, K, V> for RangeInner<'a, K, V> { 40 | fn next(&mut self) -> InnerResult<'a, K, V> { 41 | let Some(next) = self.iter.next() else { 42 | return InnerResult::Iter(None); 43 | }; 44 | let next_key = next.0.clone(); 45 | match &self.end { 46 | Bound::Included(end_key) if *end_key == next_key => InnerResult::OneMore(next), 47 | Bound::Excluded(end_key) if *end_key == next_key => InnerResult::Iter(None), 48 | Bound::Unbounded => InnerResult::Iter(Some(next)), 49 | _ => InnerResult::Iter(Some(next)), 50 | } 51 | } 52 | } 53 | 54 | impl<'a, K: KeyTrait, P: Partial, V: 'a> Iterator for Range<'a, K, V> { 55 | type Item = (K, &'a V); 56 | 57 | fn next(&mut self) -> Option<(K, &'a V)> { 58 | match self.inner.next() { 59 | InnerResult::OneMore(v) => { 60 | self.inner = Box::new(RangeInnerNone {}); 61 | Some(v) 62 | } 63 | InnerResult::Iter(i) => i, 64 | } 65 | } 66 | } 67 | 68 | impl<'a, K: KeyTrait + 'a, V> Range<'a, K, V> { 69 | pub fn empty() -> Self { 70 | Self { 71 | inner: Box::new(RangeInnerNone {}), 72 | } 73 | } 74 | 75 | pub fn for_iter(iter: Iter<'a, K, K::PartialType, V>, end: Bound) -> Self { 76 | Self { 77 | inner: Box::new(RangeInner::new(iter, end)), 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /rart/src/stats.rs: -------------------------------------------------------------------------------- 1 | use crate::node::Node; 2 | use crate::partials::Partial; 3 | use std::collections::HashMap; 4 | 5 | pub trait TreeStatsTrait { 6 | fn get_tree_stats(&self) -> TreeStats; 7 | } 8 | 9 | #[derive(Debug)] 10 | pub struct NodeStats { 11 | pub width: usize, 12 | pub total_nodes: usize, 13 | pub total_children: usize, 14 | pub density: f64, 15 | } 16 | 17 | impl Default for NodeStats { 18 | fn default() -> Self { 19 | Self { 20 | width: 0, 21 | total_nodes: 0, 22 | total_children: 0, 23 | density: 0.0, 24 | } 25 | } 26 | } 27 | 28 | #[derive(Debug)] 29 | pub struct TreeStats { 30 | pub node_stats: HashMap, 31 | pub num_leaves: usize, 32 | pub num_values: usize, 33 | pub num_inner_nodes: usize, 34 | pub total_density: f64, 35 | pub max_height: usize, 36 | } 37 | 38 | impl Default for TreeStats { 39 | fn default() -> Self { 40 | Self { 41 | node_stats: Default::default(), 42 | num_leaves: 0, 43 | num_values: 0, 44 | num_inner_nodes: 0, 45 | total_density: 0.0, 46 | max_height: 0, 47 | } 48 | } 49 | } 50 | 51 | pub(crate) fn update_tree_stats( 52 | tree_stats: &mut TreeStats, 53 | node: &NodeType, 54 | ) where 55 | NodeType: Node, 56 | PartialType: Partial, 57 | { 58 | tree_stats 59 | .node_stats 60 | .entry(node.capacity()) 61 | .and_modify(|e| { 62 | e.total_nodes += 1; 63 | e.total_children += node.num_children(); 64 | }) 65 | .or_insert(NodeStats { 66 | width: node.capacity(), 67 | total_nodes: 1, 68 | total_children: node.num_children(), 69 | density: 0.0, 70 | }); 71 | } 72 | -------------------------------------------------------------------------------- /rart/src/utils/bitarray.rs: -------------------------------------------------------------------------------- 1 | use std::mem::MaybeUninit; 2 | use std::ops::Index; 3 | 4 | use crate::utils::bitset::BitsetTrait; 5 | 6 | // BITSET_WIDTH must be RANGE_WIDTH / 16 7 | // Once generic_const_exprs is stabilized, we can use that to calculate this from a RANGE_WIDTH. 8 | // Until then, don't mess up. 9 | pub struct BitArray 10 | where 11 | BitsetType: BitsetTrait + std::default::Default, 12 | { 13 | pub(crate) bitset: BitsetType, 14 | storage: Box<[MaybeUninit; RANGE_WIDTH]>, 15 | } 16 | 17 | impl BitArray 18 | where 19 | BitsetType: BitsetTrait + std::default::Default, 20 | { 21 | pub fn new() -> Self { 22 | Self { 23 | bitset: Default::default(), 24 | storage: Box::new(unsafe { MaybeUninit::uninit().assume_init() }), 25 | } 26 | } 27 | 28 | pub fn push(&mut self, x: X) -> Option { 29 | let pos = self.bitset.first_empty()?; 30 | assert!(pos < RANGE_WIDTH); 31 | self.bitset.set(pos); 32 | unsafe { 33 | self.storage[pos].as_mut_ptr().write(x); 34 | } 35 | Some(pos) 36 | } 37 | 38 | pub fn pop(&mut self) -> Option { 39 | let pos = self.bitset.last()?; 40 | self.bitset.unset(pos); 41 | let old = std::mem::replace(&mut self.storage[pos], MaybeUninit::uninit()); 42 | Some(unsafe { old.assume_init() }) 43 | } 44 | 45 | pub fn last(&self) -> Option<&X> { 46 | self.bitset 47 | .last() 48 | .map(|pos| unsafe { self.storage[pos].assume_init_ref() }) 49 | } 50 | 51 | #[inline] 52 | pub fn last_used_pos(&self) -> Option { 53 | self.bitset.last() 54 | } 55 | 56 | #[inline] 57 | pub fn first_used(&self) -> Option { 58 | self.bitset.first_set() 59 | } 60 | 61 | #[inline] 62 | pub fn first_empty(&mut self) -> Option { 63 | // Storage size of the bitset can be larger than the range width. 64 | // For example: we have a RANGE_WIDTH of 48 and a bitset of 64x1 or 32x2. 65 | // So we need to check that the first empty bit is within the range width, or people could 66 | // get the idea they could append beyond our permitted range. 67 | let Some(first_empty) = self.bitset.first_empty() else { 68 | return None; 69 | }; 70 | if first_empty > RANGE_WIDTH { 71 | return None; 72 | } 73 | Some(first_empty) 74 | } 75 | 76 | #[inline] 77 | pub fn check(&self, pos: usize) -> bool { 78 | self.bitset.check(pos) 79 | } 80 | 81 | #[inline] 82 | pub fn get(&self, pos: usize) -> Option<&X> { 83 | assert!(pos < RANGE_WIDTH); 84 | if self.bitset.check(pos) { 85 | Some(unsafe { self.storage[pos].assume_init_ref() }) 86 | } else { 87 | None 88 | } 89 | } 90 | 91 | #[inline] 92 | pub fn get_mut(&mut self, pos: usize) -> Option<&mut X> { 93 | assert!(pos < RANGE_WIDTH); 94 | if self.bitset.check(pos) { 95 | Some(unsafe { self.storage[pos].assume_init_mut() }) 96 | } else { 97 | None 98 | } 99 | } 100 | 101 | #[inline] 102 | pub fn set(&mut self, pos: usize, x: X) { 103 | assert!(pos < RANGE_WIDTH); 104 | unsafe { 105 | self.storage[pos].as_mut_ptr().write(x); 106 | }; 107 | self.bitset.set(pos); 108 | } 109 | 110 | #[inline] 111 | pub fn update(&mut self, pos: usize, x: X) -> Option { 112 | let old = self.take_internal(pos); 113 | unsafe { 114 | self.storage[pos].as_mut_ptr().write(x); 115 | }; 116 | self.bitset.set(pos); 117 | old 118 | } 119 | 120 | #[inline] 121 | pub fn erase(&mut self, pos: usize) -> Option { 122 | let old = self.take_internal(pos)?; 123 | self.bitset.unset(pos); 124 | Some(old) 125 | } 126 | 127 | // Erase without updating index, used by update and erase 128 | #[inline] 129 | fn take_internal(&mut self, pos: usize) -> Option { 130 | assert!(pos < RANGE_WIDTH); 131 | if self.bitset.check(pos) { 132 | let old = std::mem::replace(&mut self.storage[pos], MaybeUninit::uninit()); 133 | Some(unsafe { old.assume_init() }) 134 | } else { 135 | None 136 | } 137 | } 138 | 139 | pub fn clear(&mut self) { 140 | for i in 0..RANGE_WIDTH { 141 | if self.bitset.check(i) { 142 | unsafe { self.storage[i].assume_init_drop() } 143 | } 144 | } 145 | self.bitset.clear(); 146 | } 147 | 148 | pub fn is_empty(&self) -> bool { 149 | self.bitset.is_empty() 150 | } 151 | 152 | pub fn size(&mut self) -> usize { 153 | self.bitset.size() 154 | } 155 | 156 | pub fn iter_keys(&self) -> impl DoubleEndedIterator + '_ { 157 | self.storage.iter().enumerate().filter_map(|x| { 158 | if !self.bitset.check(x.0) { 159 | None 160 | } else { 161 | Some(x.0) 162 | } 163 | }) 164 | } 165 | 166 | pub fn iter(&self) -> impl DoubleEndedIterator { 167 | self.storage.iter().enumerate().filter_map(|x| { 168 | if !self.bitset.check(x.0) { 169 | None 170 | } else { 171 | Some((x.0, unsafe { x.1.assume_init_ref() })) 172 | } 173 | }) 174 | } 175 | 176 | pub fn iter_mut(&mut self) -> impl DoubleEndedIterator { 177 | self.storage.iter_mut().enumerate().filter_map(|x| { 178 | if !self.bitset.check(x.0) { 179 | None 180 | } else { 181 | Some((x.0, unsafe { x.1.assume_init_mut() })) 182 | } 183 | }) 184 | } 185 | } 186 | 187 | impl Default for BitArray 188 | where 189 | BitsetType: BitsetTrait + std::default::Default, 190 | { 191 | fn default() -> Self { 192 | Self::new() 193 | } 194 | } 195 | 196 | impl Index for BitArray 197 | where 198 | BitsetType: BitsetTrait + std::default::Default, 199 | { 200 | type Output = X; 201 | 202 | fn index(&self, index: usize) -> &Self::Output { 203 | self.get(index).unwrap() 204 | } 205 | } 206 | 207 | impl Drop for BitArray 208 | where 209 | BitsetType: BitsetTrait + std::default::Default, 210 | { 211 | fn drop(&mut self) { 212 | for i in 0..RANGE_WIDTH { 213 | if self.bitset.check(i) { 214 | unsafe { self.storage[i].assume_init_drop() } 215 | } 216 | } 217 | self.bitset.clear(); 218 | } 219 | } 220 | 221 | #[cfg(test)] 222 | mod test { 223 | use crate::utils::bitarray::BitArray; 224 | use crate::utils::bitset::Bitset16; 225 | 226 | #[test] 227 | fn u8_vector() { 228 | let mut vec: BitArray> = BitArray::new(); 229 | assert_eq!(vec.first_empty(), Some(0)); 230 | assert_eq!(vec.last_used_pos(), None); 231 | assert_eq!(vec.push(123).unwrap(), 0); 232 | assert_eq!(vec.first_empty(), Some(1)); 233 | assert_eq!(vec.last_used_pos(), Some(0)); 234 | assert_eq!(vec.get(0), Some(&123)); 235 | assert_eq!(vec.push(124).unwrap(), 1); 236 | assert_eq!(vec.push(55).unwrap(), 2); 237 | assert_eq!(vec.push(126).unwrap(), 3); 238 | assert_eq!(vec.pop(), Some(126)); 239 | assert_eq!(vec.first_empty(), Some(3)); 240 | vec.erase(0); 241 | assert_eq!(vec.first_empty(), Some(0)); 242 | assert_eq!(vec.last_used_pos(), Some(2)); 243 | assert_eq!(vec.size(), 2); 244 | vec.set(0, 126); 245 | assert_eq!(vec.get(0), Some(&126)); 246 | assert_eq!(vec.update(0, 123), Some(126)); 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /rart/src/utils/bitset.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::min; 2 | use std::ops::Index; 3 | 4 | use num_traits::PrimInt; 5 | 6 | pub trait BitsetTrait: Default { 7 | // Total size of the bitset in bits. 8 | const BITSET_WIDTH: usize; 9 | // Total size of the bitset in bytes. 10 | const STORAGE_WIDTH_BYTES: usize; 11 | // Bit shift factor -- e.g. 3 for 8, 4 for 16, etc. 12 | const BIT_SHIFT: usize; 13 | // Bit width of each storage unit. 14 | const STORAGE_BIT_WIDTH: usize; 15 | // Total size of storage in its internal storage width (e.g. u16, u32, etc.) 16 | const STORAGE_WIDTH: usize; 17 | 18 | fn first_empty(&self) -> Option; 19 | fn first_set(&self) -> Option; 20 | fn set(&mut self, pos: usize); 21 | fn unset(&mut self, pos: usize); 22 | fn check(&self, pos: usize) -> bool; 23 | fn clear(&mut self); 24 | fn last(&self) -> Option; 25 | fn is_empty(&self) -> bool; 26 | fn size(&self) -> usize; 27 | fn bit_width(&self) -> usize; 28 | fn capacity(&self) -> usize; 29 | fn storage_width(&self) -> usize; 30 | fn as_bitmask(&self) -> u128; 31 | } 32 | 33 | pub struct Bitset 34 | where 35 | StorageType: PrimInt, 36 | { 37 | bitset: [StorageType; STORAGE_WIDTH], 38 | } 39 | 40 | impl Bitset 41 | where 42 | StorageType: PrimInt, 43 | { 44 | pub fn new() -> Self { 45 | Self { 46 | bitset: [StorageType::min_value(); STORAGE_WIDTH], 47 | } 48 | } 49 | 50 | pub fn iter(&self) -> impl Iterator + '_ { 51 | self.bitset.iter().enumerate().flat_map(|(i, b)| { 52 | (0..Self::STORAGE_BIT_WIDTH).filter_map(move |j| { 53 | let b: u64 = b.to_u64().unwrap(); 54 | if (b) & (1 << j) != 0 { 55 | Some((i << Self::BIT_SHIFT) + j) 56 | } else { 57 | None 58 | } 59 | }) 60 | }) 61 | } 62 | } 63 | 64 | impl BitsetTrait for Bitset 65 | where 66 | StorageType: PrimInt, 67 | { 68 | const BITSET_WIDTH: usize = Self::STORAGE_BIT_WIDTH * STORAGE_WIDTH; 69 | const STORAGE_WIDTH_BYTES: usize = Self::BITSET_WIDTH / 8; 70 | const BIT_SHIFT: usize = Self::STORAGE_BIT_WIDTH.trailing_zeros() as usize; 71 | const STORAGE_BIT_WIDTH: usize = std::mem::size_of::() * 8; 72 | const STORAGE_WIDTH: usize = STORAGE_WIDTH; 73 | 74 | fn first_empty(&self) -> Option { 75 | for (i, b) in self.bitset.iter().enumerate() { 76 | if b.is_zero() { 77 | return Some(i << Self::BIT_SHIFT); 78 | } 79 | if *b != StorageType::max_value() { 80 | return Some((i << Self::BIT_SHIFT) + b.trailing_ones() as usize); 81 | } 82 | } 83 | None 84 | } 85 | 86 | fn first_set(&self) -> Option { 87 | for (i, b) in self.bitset.iter().enumerate() { 88 | if !b.is_zero() { 89 | return Some((i << Self::BIT_SHIFT) + b.trailing_zeros() as usize); 90 | } 91 | } 92 | None 93 | } 94 | 95 | #[inline] 96 | fn set(&mut self, pos: usize) { 97 | assert!(pos < Self::BITSET_WIDTH); 98 | let v = self.bitset[pos >> Self::BIT_SHIFT]; 99 | let shift: StorageType = StorageType::one() << (pos % Self::STORAGE_BIT_WIDTH); 100 | let v = v.bitor(shift); 101 | self.bitset[pos >> Self::BIT_SHIFT] = v; 102 | } 103 | 104 | #[inline] 105 | fn unset(&mut self, pos: usize) { 106 | assert!(pos < Self::BITSET_WIDTH); 107 | let v = self.bitset[pos >> Self::BIT_SHIFT]; 108 | let shift = StorageType::one() << (pos % Self::STORAGE_BIT_WIDTH); 109 | let v = v & shift.not(); 110 | self.bitset[pos >> Self::BIT_SHIFT] = v; 111 | } 112 | 113 | #[inline] 114 | fn check(&self, pos: usize) -> bool { 115 | assert!(pos < Self::BITSET_WIDTH); 116 | let shift: StorageType = StorageType::one() << (pos % Self::STORAGE_BIT_WIDTH); 117 | !(self.bitset[pos >> Self::BIT_SHIFT] & shift).is_zero() 118 | } 119 | 120 | #[inline] 121 | fn clear(&mut self) { 122 | self.bitset.fill(StorageType::zero()); 123 | } 124 | 125 | fn last(&self) -> Option { 126 | for (i, b) in self.bitset.iter().enumerate() { 127 | if !b.is_zero() { 128 | return Some( 129 | (i << Self::BIT_SHIFT) + (Self::STORAGE_BIT_WIDTH - 1) 130 | - b.leading_zeros() as usize, 131 | ); 132 | } 133 | } 134 | None 135 | } 136 | 137 | fn is_empty(&self) -> bool { 138 | self.bitset.iter().all(|x| x.is_zero()) 139 | } 140 | 141 | fn size(&self) -> usize { 142 | self.bitset.iter().map(|x| x.count_ones() as usize).sum() 143 | } 144 | 145 | fn bit_width(&self) -> usize { 146 | Self::STORAGE_BIT_WIDTH 147 | } 148 | 149 | fn capacity(&self) -> usize { 150 | Self::BITSET_WIDTH 151 | } 152 | 153 | fn storage_width(&self) -> usize { 154 | Self::STORAGE_WIDTH 155 | } 156 | 157 | fn as_bitmask(&self) -> u128 { 158 | assert!(Self::STORAGE_BIT_WIDTH <= 128); 159 | let mut mask = 0u128; 160 | // copy bit-level representation, unsafe ptr copy 161 | unsafe { 162 | std::ptr::copy_nonoverlapping( 163 | self.bitset.as_ptr() as *const u8, 164 | &mut mask as *mut u128 as *mut u8, 165 | min(16, Self::STORAGE_WIDTH_BYTES), 166 | ); 167 | } 168 | mask 169 | } 170 | } 171 | 172 | impl Default for Bitset 173 | where 174 | StorageType: PrimInt, 175 | { 176 | fn default() -> Self { 177 | Self::new() 178 | } 179 | } 180 | 181 | impl Index for Bitset 182 | where 183 | StorageType: PrimInt, 184 | { 185 | type Output = bool; 186 | 187 | #[inline] 188 | fn index(&self, pos: usize) -> &Self::Output { 189 | if self.check(pos) { 190 | &true 191 | } else { 192 | &false 193 | } 194 | } 195 | } 196 | 197 | pub type Bitset64 = Bitset; 198 | pub type Bitset32 = Bitset; 199 | pub type Bitset16 = Bitset; 200 | pub type Bitset8 = Bitset; 201 | 202 | #[cfg(test)] 203 | mod tests { 204 | use crate::utils::bitset::BitsetTrait; 205 | 206 | #[test] 207 | fn test_first_free_8s() { 208 | let mut bs = super::Bitset8::<4>::new(); 209 | bs.set(1); 210 | bs.set(3); 211 | assert_eq!(bs.first_empty(), Some(0)); 212 | bs.set(0); 213 | assert_eq!(bs.first_empty(), Some(2)); 214 | 215 | // Now fill it up and verify none. 216 | for i in 0..bs.capacity() { 217 | bs.set(i); 218 | } 219 | assert_eq!(bs.first_empty(), None); 220 | } 221 | 222 | #[test] 223 | fn test_first_free_8_2() { 224 | let mut bs = super::Bitset8::<2>::new(); 225 | bs.set(1); 226 | bs.set(3); 227 | assert_eq!(bs.first_empty(), Some(0)); 228 | bs.set(0); 229 | assert_eq!(bs.first_empty(), Some(2)); 230 | 231 | // Now fill it up and verify none. 232 | for i in 0..bs.capacity() { 233 | bs.set(i); 234 | } 235 | assert_eq!(bs.first_empty(), None); 236 | } 237 | 238 | #[test] 239 | fn test_first_free_32s() { 240 | let mut bs = super::Bitset32::<1>::new(); 241 | bs.set(1); 242 | bs.set(3); 243 | assert_eq!(bs.first_empty(), Some(0)); 244 | bs.set(0); 245 | assert_eq!(bs.first_empty(), Some(2)); 246 | 247 | for i in 0..bs.capacity() { 248 | bs.set(i); 249 | } 250 | assert_eq!(bs.first_empty(), None); 251 | } 252 | 253 | #[test] 254 | fn test_iter_16s() { 255 | let mut bs = super::Bitset16::<4>::new(); 256 | bs.set(0); 257 | bs.set(1); 258 | bs.set(2); 259 | bs.set(4); 260 | bs.set(8); 261 | bs.set(16); 262 | let v: Vec = bs.iter().collect(); 263 | assert_eq!(v, vec![0, 1, 2, 4, 8, 16]); 264 | } 265 | 266 | #[test] 267 | fn test_first_free_64s() { 268 | let mut bs = super::Bitset64::<4>::new(); 269 | bs.set(1); 270 | bs.set(3); 271 | assert_eq!(bs.first_empty(), Some(0)); 272 | bs.set(0); 273 | assert_eq!(bs.first_empty(), Some(2)); 274 | } 275 | 276 | #[test] 277 | fn test_iter_64s() { 278 | let mut bs = super::Bitset64::<4>::new(); 279 | bs.set(0); 280 | bs.set(1); 281 | bs.set(2); 282 | bs.set(4); 283 | bs.set(8); 284 | bs.set(16); 285 | bs.set(32); 286 | bs.set(47); 287 | bs.set(48); 288 | bs.set(49); 289 | bs.set(127); 290 | let v: Vec = bs.iter().collect(); 291 | assert_eq!(v, vec![0, 1, 2, 4, 8, 16, 32, 47, 48, 49, 127]); 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /rart/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | use std::cell::Cell; 2 | use std::marker::PhantomData; 3 | use std::sync::MutexGuard; 4 | 5 | pub mod bitarray; 6 | pub mod bitset; 7 | pub mod optimistic_lock; 8 | pub mod u8_keys; 9 | 10 | pub type PhantomUnsync = PhantomData>; 11 | pub type PhantomUnsend = PhantomData>; 12 | -------------------------------------------------------------------------------- /rart/src/utils/optimistic_lock.rs: -------------------------------------------------------------------------------- 1 | use std::cell::UnsafeCell; 2 | use std::fmt::{Display, Formatter}; 3 | use std::ops::{Deref, DerefMut}; 4 | use std::sync::atomic::{AtomicU64, Ordering}; 5 | 6 | use crossbeam_utils::Backoff; 7 | 8 | use crate::utils::PhantomUnsend; 9 | 10 | pub const DEFAULT_MAX_RETRIES: u8 = 100; 11 | 12 | #[derive(Debug, Eq, PartialEq)] 13 | pub enum LockError { 14 | Locked, 15 | 16 | Retry, 17 | 18 | MaybeDeadlock(usize), 19 | } 20 | 21 | impl Display for LockError { 22 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 23 | match self { 24 | LockError::Locked => write!(f, "Locked"), 25 | LockError::Retry => write!(f, "Retry"), 26 | LockError::MaybeDeadlock(num_tries) => write!(f, "MaybeDeadlock(tries: {})", num_tries), 27 | } 28 | } 29 | } 30 | 31 | impl std::error::Error for LockError {} 32 | 33 | // Optimistic lock. 34 | // Encodes a version and a lock into the same atomic. 35 | // Reads are optimistic, but have to be retried if the version changes as a result of a write. 36 | pub struct OptimisticLock { 37 | // 63 bits for the version, 1 bit for the lock 38 | version_and_lock: AtomicU64, 39 | storage: UnsafeCell, 40 | max_retries: u8, 41 | } 42 | 43 | impl OptimisticLock { 44 | pub fn new(storage: V) -> Self { 45 | Self { 46 | version_and_lock: AtomicU64::new(2), 47 | storage: UnsafeCell::new(storage), 48 | max_retries: DEFAULT_MAX_RETRIES, 49 | } 50 | } 51 | 52 | pub fn with_max_retries(storage: V, max_retries: usize) -> Self { 53 | Self { 54 | version_and_lock: AtomicU64::new(2), 55 | storage: UnsafeCell::new(storage), 56 | max_retries: max_retries as u8, 57 | } 58 | } 59 | 60 | pub fn version(&self) -> u64 { 61 | self.version_and_lock.load(Ordering::Relaxed) 62 | } 63 | 64 | pub fn read(&self) -> Result, LockError> { 65 | ReadGuard::new(self) 66 | } 67 | 68 | pub fn read_perform( 69 | &self, 70 | read_function: ReadFunction, 71 | ) -> Result 72 | where 73 | ReadFunction: Fn(&V) -> ReadResult, 74 | { 75 | let backoff = Backoff::new(); 76 | let mut tries = 0; 77 | 78 | // Repeatedly perform operation 'f' on the value, until the version is stable (should be 79 | // once). 80 | loop { 81 | if tries > self.max_retries { 82 | return Err(LockError::MaybeDeadlock(tries as usize)); 83 | } 84 | 85 | let guard = self.read()?; 86 | let present_value = &*guard; 87 | let operation_result = (read_function)(present_value); 88 | match guard.check_version() { 89 | Ok(_) => return Ok(operation_result), 90 | Err(LockError::Retry) => { 91 | tries += 1; 92 | backoff.spin(); 93 | continue; 94 | } 95 | Err(e) => return Err(e), 96 | } 97 | } 98 | } 99 | 100 | pub fn read_write_perform( 101 | &mut self, 102 | read_function: ReadFunction, 103 | write_function: WriteFunction, 104 | ) -> Result<(), LockError> 105 | where 106 | ReadFunction: Fn(&V) -> V, 107 | WriteFunction: Fn(&V) -> V, 108 | { 109 | let backoff = Backoff::new(); 110 | let mut tries = 0; 111 | 112 | // Repeatedly perform operation 'f' on the value, until the version is stable (should be 113 | // once). 114 | loop { 115 | if tries > self.max_retries { 116 | return Err(LockError::MaybeDeadlock(tries as usize)); 117 | } 118 | 119 | let guard = self.read()?; 120 | let present_value = &*guard; 121 | // Perform the read. 122 | let operation_result = (read_function)(present_value); 123 | 124 | // Now perform the write function, but locking only with the exact version we 125 | // read. If that fails, we'll retry the whole thing. 126 | match self.write_with(guard.version) { 127 | Ok(mut g) => { 128 | let v = (write_function)(&operation_result); 129 | *g = v; 130 | return Ok(()); 131 | } 132 | Err(LockError::Retry) => { 133 | tries += 1; 134 | backoff.spin(); 135 | continue; 136 | } 137 | Err(e) => return Err(e), 138 | }; 139 | } 140 | } 141 | 142 | pub fn write(&self) -> Result, LockError> { 143 | let version = self.probe_lock()?; 144 | self.write_with(version) 145 | } 146 | 147 | pub fn write_with(&self, version: u64) -> Result, LockError> { 148 | match self.version_and_lock.compare_exchange( 149 | version, 150 | version + 0b1, 151 | Ordering::Acquire, 152 | Ordering::Acquire, 153 | ) { 154 | Ok(_) => Ok(WriteGuard::new(self)), 155 | Err(_) => Err(LockError::Retry), 156 | } 157 | } 158 | 159 | fn probe_lock(&self) -> Result { 160 | let version_lock = self.version_and_lock.load(Ordering::Acquire); 161 | if version_lock & 0x1 == 1 { 162 | return Err(LockError::Locked); 163 | } 164 | Ok(version_lock) 165 | } 166 | } 167 | 168 | pub struct ReadGuard<'a, V: 'a> { 169 | coupling: &'a OptimisticLock, 170 | version: u64, 171 | _unsend_marker: PhantomUnsend, 172 | } 173 | 174 | impl<'a, V: 'a> ReadGuard<'a, V> { 175 | fn new(coupling: &'a OptimisticLock) -> Result { 176 | let version = coupling.probe_lock()?; 177 | Ok(Self { 178 | coupling, 179 | version, 180 | _unsend_marker: Default::default(), 181 | }) 182 | } 183 | fn check_version(self) -> Result { 184 | if self.version == self.coupling.probe_lock()? { 185 | Ok(self.version) 186 | } else { 187 | Err(LockError::Retry) 188 | } 189 | } 190 | } 191 | 192 | impl<'a, V> Deref for ReadGuard<'a, V> { 193 | type Target = V; 194 | fn deref(&self) -> &Self::Target { 195 | unsafe { &*self.coupling.storage.get() } 196 | } 197 | } 198 | 199 | pub struct WriteGuard<'a, V: 'a> { 200 | coupling: &'a OptimisticLock, 201 | } 202 | 203 | impl<'a, V: 'a> WriteGuard<'a, V> { 204 | fn new(coupling: &'a OptimisticLock) -> Self { 205 | Self { coupling } 206 | } 207 | } 208 | 209 | impl<'a, V: 'a> Drop for WriteGuard<'a, V> { 210 | fn drop(&mut self) { 211 | self.coupling 212 | .version_and_lock 213 | .fetch_add(1, Ordering::Release); 214 | } 215 | } 216 | 217 | impl<'a, V: 'a> Deref for WriteGuard<'a, V> { 218 | type Target = V; 219 | 220 | #[inline(always)] 221 | fn deref(&self) -> &Self::Target { 222 | unsafe { &*self.coupling.storage.get() } 223 | } 224 | } 225 | 226 | impl<'a, V: 'a> DerefMut for WriteGuard<'a, V> { 227 | #[inline(always)] 228 | fn deref_mut(&mut self) -> &mut Self::Target { 229 | unsafe { &mut *self.coupling.storage.get() } 230 | } 231 | } 232 | 233 | #[cfg(test)] 234 | mod tests { 235 | use crate::utils::optimistic_lock::{LockError, OptimisticLock}; 236 | 237 | #[test] 238 | fn test_read() { 239 | let v = OptimisticLock::new(0); 240 | { 241 | let r = v.read().unwrap(); 242 | assert_eq!(*r, 0); 243 | assert!(r.check_version().is_ok()); 244 | } 245 | } 246 | 247 | #[test] 248 | fn test_write_read() { 249 | let v = OptimisticLock::new(0); 250 | { 251 | let mut w = v.write().unwrap(); 252 | assert_eq!(*w, 0); 253 | *w = 1; 254 | } 255 | { 256 | let r = v.read().unwrap(); 257 | assert_eq!(*r, 1); 258 | assert!(r.check_version().is_ok()); 259 | } 260 | } 261 | 262 | #[test] 263 | fn test_out_of_sync() { 264 | let v = OptimisticLock::new(0); 265 | { 266 | let r = v.read().unwrap(); 267 | assert_eq!(*r, 0); 268 | { 269 | let mut w = v.write().unwrap(); 270 | *w = 1; 271 | } 272 | assert_eq!(r.check_version(), Err(LockError::Retry)); 273 | } 274 | } 275 | 276 | #[test] 277 | fn test_concurrent_write_with_retry() { 278 | static mut LOCK: Option> = None; 279 | unsafe { LOCK = Some(OptimisticLock::new(0)) }; 280 | let per_thread_increments = 10000; 281 | let num_threads = 10; 282 | let do_add = move || { 283 | for _ in 0..per_thread_increments { 284 | loop { 285 | unsafe { 286 | match LOCK.as_ref().unwrap().write() { 287 | Ok(mut guard) => { 288 | *guard += 1; 289 | break; 290 | } 291 | Err(LockError::Retry) => { 292 | continue; 293 | } 294 | Err(LockError::Locked) => {} 295 | Err(LockError::MaybeDeadlock(num_tries)) => { 296 | panic!("deadlock after {}, should not happen", num_tries) 297 | } 298 | } 299 | } 300 | } 301 | } 302 | }; 303 | let add_threads = (0..num_threads) 304 | .map(|_| std::thread::spawn(do_add)) 305 | .collect::>(); 306 | for t in add_threads { 307 | t.join().unwrap(); 308 | } 309 | unsafe { 310 | let read = LOCK.as_ref().unwrap().read().unwrap(); 311 | assert_eq!(*read, per_thread_increments * num_threads); 312 | read.check_version().unwrap(); 313 | } 314 | } 315 | 316 | #[test] 317 | fn test_concurrent_write_retry_with_closure() { 318 | static mut LOCKS: Option> = None; 319 | unsafe { LOCKS = Some(OptimisticLock::new(0)) }; 320 | let per_thread_increments = 10000; 321 | let num_threads = 10; 322 | let do_add = move || { 323 | for _ in 0..per_thread_increments { 324 | loop { 325 | unsafe { 326 | let op_result = LOCKS 327 | .as_mut() 328 | .unwrap() 329 | .read_write_perform(|v| *v, |v| v + 1); 330 | match op_result { 331 | Ok(_) => { 332 | break; 333 | } 334 | Err(LockError::Retry) => { 335 | continue; 336 | } 337 | Err(LockError::Locked) => { 338 | continue; 339 | } 340 | Err(LockError::MaybeDeadlock(num_tries)) => { 341 | panic!("deadlock after {} tries, should not happen", num_tries) 342 | } 343 | } 344 | } 345 | } 346 | } 347 | }; 348 | let add_threads = (0..num_threads) 349 | .map(|_| std::thread::spawn(do_add)) 350 | .collect::>(); 351 | for t in add_threads { 352 | t.join().unwrap(); 353 | } 354 | unsafe { 355 | let read = LOCKS.as_ref().unwrap().read().unwrap(); 356 | assert_eq!(*read, per_thread_increments * num_threads); 357 | read.check_version().unwrap(); 358 | } 359 | } 360 | } 361 | -------------------------------------------------------------------------------- /rart/src/utils/u8_keys.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::bitset::BitsetTrait; 2 | 3 | #[cfg(feature = "simd_keys")] 4 | mod simd_keys { 5 | use simdeez::*; 6 | use simdeez::{prelude::*, simd_runtime_generate}; 7 | 8 | simd_runtime_generate!( 9 | pub fn simdeez_find_insert_pos(key: u8, keys: &[u8], ff_mask_out: u32) -> Option { 10 | let key_cmp_vec = S::Vi8::set1(key as i8); 11 | let key_vec = SimdBaseIo::load_from_ptr_unaligned(keys.as_ptr() as *const i8); 12 | let results = key_cmp_vec.cmp_lt(key_vec); 13 | let bitfield = results.get_mask() & (ff_mask_out as u32); 14 | if bitfield != 0 { 15 | let idx = bitfield.trailing_zeros() as usize; 16 | return Some(idx); 17 | } 18 | None 19 | } 20 | ); 21 | 22 | simd_runtime_generate!( 23 | pub fn simdeez_find_key(key: u8, keys: &[u8], ff_mask_out: u32) -> Option { 24 | let key_cmp_vec = S::Vi8::set1(key as i8); 25 | let key_vec = SimdBaseIo::load_from_ptr_unaligned(keys.as_ptr() as *const i8); 26 | let results = key_cmp_vec.cmp_eq(key_vec); 27 | let bitfield = results.get_mask() & (ff_mask_out as u32); 28 | if bitfield != 0 { 29 | let idx = bitfield.trailing_zeros() as usize; 30 | return Some(idx); 31 | } 32 | None 33 | } 34 | ); 35 | } 36 | 37 | #[cfg(not(feature = "simd_keys"))] 38 | fn binary_find_key(key: u8, keys: &[u8], num_children: usize) -> Option { 39 | let mut left = 0; 40 | let mut right = num_children; 41 | while left < right { 42 | let mid = (left + right) / 2; 43 | match keys[mid].cmp(&key) { 44 | std::cmp::Ordering::Less => left = mid + 1, 45 | std::cmp::Ordering::Equal => return Some(mid), 46 | std::cmp::Ordering::Greater => right = mid, 47 | } 48 | } 49 | None 50 | } 51 | 52 | pub fn u8_keys_find_key_position_sorted( 53 | key: u8, 54 | keys: &[u8], 55 | num_children: usize, 56 | ) -> Option { 57 | // Width 4 and under, just use linear search. 58 | if WIDTH <= 4 { 59 | return (0..num_children).find(|&i| keys[i] == key); 60 | } 61 | 62 | #[cfg(feature = "simd_keys")] 63 | { 64 | simd_keys::simdeez_find_key(key, keys, (1 << num_children) - 1) 65 | } 66 | 67 | // Fallback to binary search. 68 | #[cfg(not(feature = "simd_keys"))] 69 | binary_find_key(key, keys, num_children) 70 | } 71 | 72 | pub fn u8_keys_find_insert_position_sorted( 73 | key: u8, 74 | keys: &[u8], 75 | num_children: usize, 76 | ) -> Option { 77 | #[cfg(feature = "simd_keys")] 78 | { 79 | simd_keys::simdeez_find_insert_pos(key, keys, (1 << num_children) - 1) 80 | .or(Some(num_children)) 81 | } 82 | 83 | // Fallback: use linear search to find the insertion point. 84 | #[cfg(not(feature = "simd_keys"))] 85 | (0..num_children) 86 | .rev() 87 | .find(|&i| key < keys[i]) 88 | .or(Some(num_children)) 89 | } 90 | 91 | pub fn u8_keys_find_key_position( 92 | key: u8, 93 | keys: &[u8], 94 | children_bitmask: &Bitset, 95 | ) -> Option { 96 | // SIMD optimized 97 | #[cfg(feature = "simd_keys")] 98 | { 99 | // Special 0xff key is special 100 | let mut mask = (1 << WIDTH) - 1; 101 | if key == 255 { 102 | mask &= children_bitmask.as_bitmask() as u32; 103 | } 104 | simd_keys::simdeez_find_key(key, keys, mask) 105 | } 106 | 107 | #[cfg(not(feature = "simd_keys"))] 108 | { 109 | // Fallback to linear search for non-SIMD. 110 | for (i, k) in keys.iter().enumerate() { 111 | if key == 255 && !children_bitmask.check(i) { 112 | continue; 113 | } 114 | if *k == key { 115 | return Some(i); 116 | } 117 | } 118 | None 119 | } 120 | } 121 | --------------------------------------------------------------------------------