├── .cargo ├── katex.header.html ├── katex.script.html ├── mermaid.header.html └── mermaid.script.html ├── .clippy.toml ├── .config └── nextest.toml ├── .editorconfig ├── .envrc ├── .github ├── copilot-instructions.md ├── renovate.json └── workflows │ └── test.yml ├── .gitignore ├── .markdownlint-cli2.yaml ├── .pre-commit-config.yaml ├── .rustfmt.toml ├── .taplo.toml ├── .yamlfix.toml ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── benches ├── btreemap.rs ├── hashmap.rs ├── linkedlist.rs ├── main.rs ├── ordered_skiplist.rs ├── skiplist.rs ├── skipmap.rs ├── vec.rs └── vecdeque.rs ├── cliff.toml ├── committed.toml ├── criterion.toml └── src ├── level_generator.rs ├── level_generator └── geometric.rs ├── lib.rs ├── ordered_skiplist.rs ├── skiplist.rs ├── skipmap.rs └── skipnode.rs /.cargo/katex.header.html: -------------------------------------------------------------------------------- 1 | 8 | 14 | -------------------------------------------------------------------------------- /.cargo/katex.script.html: -------------------------------------------------------------------------------- 1 | 63 | -------------------------------------------------------------------------------- /.cargo/mermaid.header.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /.cargo/mermaid.script.html: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /.clippy.toml: -------------------------------------------------------------------------------- 1 | #:schema https://json.schemastore.org/any.json 2 | 3 | # Clippy's default only allows 2 segments, but I find 3 to be allowable before 4 | # requiring a 'use' statement. 5 | absolute-paths-max-segments = 3 6 | 7 | # Tweaks specific targeting tests 8 | allow-expect-in-tests = true 9 | allow-panic-in-tests = true 10 | allow-print-in-tests = true 11 | allow-unwrap-in-tests = false 12 | 13 | disallowed-methods = [ 14 | # Prefer the pretty_assertions crate 15 | "std::assert_eq", 16 | "std::assert_matches::assert_matches", 17 | "std::assert_ne", 18 | ] 19 | 20 | # Prefer semi-colons inside blocks _except_ for single-line blocks 21 | semicolon-inside-block-ignore-singleline = true 22 | semicolon-outside-block-ignore-multiline = true 23 | -------------------------------------------------------------------------------- /.config/nextest.toml: -------------------------------------------------------------------------------- 1 | #:schema https://json.schemastore.org/any.json 2 | [profile.ci] 3 | # In CI, we want to run all tests instead of failing fast 4 | fail-fast = false 5 | 6 | # Timeout after 5 periods of 60 seconds 7 | slow-timeout = { period = "60s", terminate-after = 5 } 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | indent_size = 2 10 | indent_style = space 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.md] 15 | indent_size = 4 16 | 17 | [*.rs] 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | export RUSTDOCFLAGS="${RUSTDOCFLAGS:-} --html-in-header $PWD/.cargo/katex.header.html" 3 | export RUSTDOCFLAGS="${RUSTDOCFLAGS:-} --html-in-header $PWD/.cargo/mermaid.header.html" 4 | export RUSTDOCFLAGS="${RUSTDOCFLAGS:-} --html-after-content $PWD/.cargo/katex.script.html" 5 | export RUSTDOCFLAGS="${RUSTDOCFLAGS:-} --html-after-content $PWD/.cargo/mermaid.script.html" 6 | -------------------------------------------------------------------------------- /.github/copilot-instructions.md: -------------------------------------------------------------------------------- 1 | - We use `cargo nextest run` to run tests. 2 | - When referencing modules within the same crate, prefer `crate::module::function` over `super::module::function`. 3 | - Use `cargo +nightly fmt` to format your code. 4 | - We avoid introducing panics where possible. If you must introduce a panic, prefer `expect` over `unwrap`. 5 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:best-practices"], 4 | "timezone": "Australia/Melbourne", 5 | "pre-commit": { 6 | "enabled": true 7 | }, 8 | "git-submodules": { 9 | "enabled": true 10 | }, 11 | "prHourlyLimit": 0, 12 | "prConcurrentLimit": 0, 13 | "automerge": true, 14 | "packageRules": [ 15 | { 16 | "matchPackageNames": ["taiki-e/install-action"], 17 | "schedule": "before 4am on monday" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: test 3 | 4 | permissions: 5 | contents: read 6 | 7 | on: 8 | pull_request: 9 | branches: 10 | - main 11 | push: 12 | branches: 13 | - main 14 | 15 | concurrency: 16 | group: ${{ github.workflow }}-${{ github.ref }} 17 | cancel-in-progress: true 18 | 19 | env: 20 | # Generic 21 | FORCE_COLOR: '1' 22 | CLICOLOR: '1' 23 | # Rust 24 | CARGO_TERM_COLOR: always 25 | RUST_BACKTRACE: '1' 26 | NEXTEST_PROFILE: ci 27 | 28 | jobs: 29 | complete: 30 | name: Test completion check 31 | if: always() 32 | 33 | permissions: 34 | contents: none 35 | 36 | runs-on: ubuntu-latest 37 | needs: 38 | - clippy 39 | - coverage 40 | - format 41 | - pre-commit 42 | - spelling 43 | - test 44 | 45 | steps: 46 | - name: Failed 47 | run: exit 1 48 | if: > 49 | contains(needs.*.result, 'failure') 50 | || contains(needs.*.result, 'cancelled') 51 | || contains(needs.*.result, 'skipped') 52 | 53 | clippy: 54 | name: >- 55 | Clippy Rust ${{ matrix.rust }} 56 | on ${{ startsWith(matrix.os, 'macos-') && 'macOS' || startsWith(matrix.os, 'windows-') && 'Windows' || 'Linux' }} 57 | 58 | runs-on: ${{ matrix.os }} 59 | 60 | strategy: 61 | matrix: 62 | os: 63 | - ubuntu-latest 64 | rust: 65 | - stable 66 | continue-on-error: ${{ matrix.rust != 'stable' }} 67 | 68 | steps: 69 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 70 | 71 | - name: Install Rust 72 | uses: dtolnay/rust-toolchain@stable 73 | with: 74 | toolchain: ${{ matrix.rust }} 75 | components: clippy 76 | 77 | - name: Cache Rust 78 | uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8 79 | 80 | - name: Install cargo-hack and cargo-action-fmt 81 | uses: taiki-e/install-action@735e5933943122c5ac182670a935f54a949265c1 # v2.52.4 82 | with: 83 | tool: cargo-hack,cargo-action-fmt 84 | 85 | - name: Clippy 86 | run: | 87 | cargo hack \ 88 | --feature-powerset \ 89 | clippy \ 90 | --workspace \ 91 | --all-targets \ 92 | --message-format json \ 93 | -- \ 94 | -D warnings | 95 | cargo-action-fmt 96 | 97 | format: 98 | name: >- 99 | Format Rust ${{ matrix.rust }} 100 | on ${{ startsWith(matrix.os, 'macos-') && 'macOS' || startsWith(matrix.os, 'windows-') && 'Windows' || 'Linux' }} 101 | 102 | runs-on: ${{ matrix.os }} 103 | 104 | strategy: 105 | matrix: 106 | os: 107 | - ubuntu-latest 108 | rust: 109 | - stable 110 | continue-on-error: ${{ matrix.rust != 'stable' }} 111 | 112 | steps: 113 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 114 | 115 | - name: Install Rust 116 | uses: dtolnay/rust-toolchain@stable 117 | with: 118 | toolchain: ${{ matrix.rust }} 119 | components: rustfmt 120 | 121 | - name: Cache Rust 122 | uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8 123 | 124 | - name: Format 125 | run: cargo fmt --check 126 | 127 | test: 128 | name: >- 129 | Test Rust ${{ matrix.rust }} 130 | on ${{ startsWith(matrix.os, 'macos-') && 'macOS' || startsWith(matrix.os, 'windows-') && 'Windows' || 'Linux' }} 131 | 132 | runs-on: ${{ matrix.os }} 133 | 134 | strategy: 135 | matrix: 136 | os: 137 | - ubuntu-latest 138 | - windows-latest 139 | - macos-latest 140 | rust: 141 | - stable 142 | - beta 143 | continue-on-error: ${{ matrix.rust != 'stable' }} 144 | 145 | steps: 146 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 147 | 148 | - name: Install Rust 149 | uses: dtolnay/rust-toolchain@stable 150 | with: 151 | toolchain: ${{ matrix.rust }} 152 | 153 | - name: Cache Rust 154 | uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8 155 | 156 | - name: Install cargo-hack and nextest 157 | uses: taiki-e/install-action@735e5933943122c5ac182670a935f54a949265c1 # v2.52.4 158 | with: 159 | tool: cargo-hack,nextest 160 | 161 | - name: Run tests 162 | shell: bash 163 | env: 164 | NEXTEST_EXPERIMENTAL_LIBTEST_JSON: '1' 165 | run: | 166 | while IFS=$'\n' read -r line || ! retval=$line; do 167 | if ! echo "$line" | jq -e . 2>/dev/null 1>&2; then 168 | continue 169 | fi 170 | 171 | type=$(echo "$line" | jq -r '.type') 172 | event=$(echo "$line" | jq -r '.event') 173 | if [ "$type" != "test" ] || [ "$event" != "failed" ]; then 174 | continue 175 | fi 176 | 177 | echo "Found failed test: $line" 178 | 179 | failed=true 180 | name=$(echo "$line" | jq -r '.name') 181 | stdout=$(echo "$line" | jq -r '.stdout') 182 | 183 | { 184 | echo "## Failed test $name" 185 | echo '```' 186 | echo "$stdout" 187 | echo '```' 188 | } >>"$GITHUB_STEP_SUMMARY" 189 | done < <( 190 | cargo hack nextest run \ 191 | --feature-powerset \ 192 | --workspace \ 193 | --message-format libtest-json 194 | printf '%s' "$?" 195 | ) 196 | 197 | exit "$retval" 198 | 199 | coverage: 200 | name: >- 201 | Coverage ${{ matrix.rust }} 202 | on ${{ startsWith(matrix.os, 'macos-') && 'macOS' || startsWith(matrix.os, 'windows-') && 'Windows' || 'Linux' }} 203 | 204 | runs-on: ${{ matrix.os }} 205 | 206 | strategy: 207 | matrix: 208 | os: 209 | - ubuntu-latest 210 | rust: 211 | - nightly 212 | continue-on-error: ${{ matrix.rust != 'stable' }} 213 | 214 | steps: 215 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 216 | 217 | - name: Install Rust 218 | uses: dtolnay/rust-toolchain@stable 219 | with: 220 | toolchain: ${{ matrix.rust }} 221 | 222 | - name: Cache Rust 223 | uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8 224 | 225 | - name: Install cargo-hack, tarpaulin 226 | uses: taiki-e/install-action@735e5933943122c5ac182670a935f54a949265c1 # v2.52.4 227 | with: 228 | tool: cargo-hack,cargo-tarpaulin 229 | 230 | - name: Run tests 231 | shell: bash 232 | env: 233 | NEXTEST_EXPERIMENTAL_LIBTEST_JSON: '1' 234 | run: | 235 | cargo hack \ 236 | --feature-powerset \ 237 | --workspace \ 238 | tarpaulin \ 239 | --out Xml 240 | 241 | spelling: 242 | name: Spell check 243 | 244 | runs-on: ubuntu-latest 245 | 246 | steps: 247 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 248 | 249 | - name: Spell Check Repo 250 | uses: crate-ci/typos@b1ae8d918b6e85bd611117d3d9a3be4f903ee5e4 # v1.33.1 251 | 252 | pre-commit: 253 | name: Pre-commit 254 | 255 | runs-on: ubuntu-latest 256 | 257 | env: 258 | PRE_COMMIT_HOME: ${{ github.workspace }}/.pre-commit 259 | 260 | steps: 261 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 262 | 263 | - name: Cache pre-commit 264 | uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 265 | with: 266 | path: | 267 | ${{ env.PRE_COMMIT_HOME }} 268 | key: ${{ runner.os }}-pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} 269 | restore-keys: | 270 | ${{ runner.os }}-pre-commit- 271 | 272 | - name: Install Rust 273 | uses: dtolnay/rust-toolchain@stable 274 | with: 275 | components: rustfmt, clippy 276 | 277 | - name: Set up uv 278 | uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0 279 | with: 280 | enable-cache: true 281 | cache-suffix: pre-commit 282 | cache-dependency-glob: '' 283 | 284 | - name: Install pre-commit 285 | run: uv tool install pre-commit 286 | - name: Install hatch 287 | run: uv tool install hatch 288 | 289 | - name: Run pre-commit 290 | run: pre-commit run --show-diff-on-failure --color=always --all-files 291 | env: 292 | OPENAI_API_KEY: sk-000000000000000000000000000000000000000000000000 293 | 294 | committed: 295 | name: Committed 296 | 297 | runs-on: ubuntu-latest 298 | if: github.event_name == 'pull_request' 299 | 300 | steps: 301 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 302 | with: 303 | fetch-depth: 0 304 | 305 | - name: committed-action 306 | uses: crate-ci/committed@15229711f8f597474c0b636f327cde5969f9a529 # v1.1.7 307 | with: 308 | args: -vv --no-merge-commit --no-wip --no-fixup 309 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | /criterion 4 | 5 | .vscode/ 6 | -------------------------------------------------------------------------------- /.markdownlint-cli2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | ignores: 3 | - .github/copilot-instructions.md 4 | 5 | config: 6 | default: true 7 | 8 | # Do not enforce line length 9 | line-length: false 10 | 11 | # Adjust list indentation for 4 spaces 12 | list-marker-space: 13 | ul_single: 3 14 | ul_multi: 3 15 | ol_single: 2 16 | ol_multi: 2 17 | 18 | ul-indent: 19 | indent: 4 20 | 21 | # Require fenced code blocks 22 | code-block-style: 23 | style: fenced 24 | 25 | # Disable checking for reference links, as MkDocs generates additional ones that 26 | # are not visible to MarkdownLint. 27 | reference-links-images: false 28 | 29 | strong-style: 30 | style: asterisk 31 | 32 | emphasis-style: 33 | style: underscore 34 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | default_install_hook_types: 3 | - commit-msg 4 | - post-checkout 5 | - pre-commit 6 | - pre-push 7 | - prepare-commit-msg 8 | 9 | # Unless otherwise specified, all hooks below are run during pre-commit. 10 | default_stages: 11 | - pre-commit 12 | 13 | repos: 14 | # Generic hooks that apply to a lot of files 15 | - repo: https://github.com/pre-commit/pre-commit-hooks 16 | rev: v5.0.0 17 | hooks: 18 | - id: check-added-large-files 19 | - id: check-case-conflict 20 | - id: check-executables-have-shebangs 21 | - id: check-shebang-scripts-are-executable 22 | exclude: | 23 | (?x)^( 24 | # The //! used in Rust is not a shebang 25 | .*\.rs 26 | )$ 27 | - id: check-symlinks 28 | - id: destroyed-symlinks 29 | - id: end-of-file-fixer 30 | - id: mixed-line-ending 31 | - id: trailing-whitespace 32 | exclude: | 33 | (?x)^( 34 | # Snaps are verbatim 35 | | .*/snapshots/.*\.snap 36 | ) 37 | 38 | # The following only check that the files are parseable and does _not_ 39 | # modify the formatting. 40 | - id: check-toml 41 | - id: check-xml 42 | 43 | - repo: https://github.com/lyz-code/yamlfix/ 44 | rev: 1.17.0 45 | hooks: 46 | - id: yamlfix 47 | args: 48 | - --config-file 49 | - .yamlfix.toml 50 | 51 | - repo: https://gitlab.com/bmares/check-json5 52 | rev: v1.0.0 53 | hooks: 54 | # As above, this only checks for valid JSON files. This implementation 55 | # allows for comments within JSON files. 56 | - id: check-json5 57 | 58 | - repo: https://github.com/biomejs/pre-commit 59 | rev: v1.9.4 60 | hooks: 61 | - id: biome-check 62 | 63 | - repo: https://github.com/ComPWA/taplo-pre-commit 64 | rev: v0.9.3 65 | hooks: 66 | - id: taplo-format 67 | - id: taplo-lint 68 | 69 | - repo: https://github.com/crate-ci/committed 70 | rev: v1.1.7 71 | hooks: 72 | - id: committed 73 | 74 | - repo: https://github.com/DavidAnson/markdownlint-cli2 75 | rev: v0.18.1 76 | hooks: 77 | - id: markdownlint-cli2 78 | args: 79 | - --fix 80 | 81 | - repo: https://github.com/crate-ci/typos 82 | rev: v1.33.1 83 | hooks: 84 | - id: typos 85 | 86 | - repo: https://github.com/shellcheck-py/shellcheck-py 87 | rev: v0.10.0.1 88 | hooks: 89 | - id: shellcheck 90 | args: 91 | - --external-sources 92 | 93 | - repo: https://github.com/scop/pre-commit-shfmt 94 | rev: v3.9.0-1 95 | hooks: 96 | - id: shfmt 97 | 98 | - repo: local 99 | hooks: 100 | - id: cargo-fmt 101 | name: cargo fmt 102 | entry: cargo fmt -- 103 | language: system 104 | types: 105 | - rust 106 | pass_filenames: false # This makes it a lot faster 107 | 108 | - id: cargo-clippy 109 | name: cargo clippy 110 | entry: cargo clippy -- 111 | language: system 112 | types: 113 | - rust 114 | pass_filenames: false 115 | 116 | - id: cargo-check 117 | name: cargo check 118 | entry: cargo check -- 119 | language: system 120 | types: 121 | - rust 122 | pass_filenames: false 123 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | #:schema https://json.schemastore.org/rustfmt.json 2 | # Used by `cargo +nightly fmt`, otherwise emits a warning 3 | unstable_features = true 4 | 5 | ################################################################################ 6 | ## Imports 7 | ################################################################################ 8 | group_imports = "StdExternalCrate" 9 | imports_granularity = "Crate" 10 | reorder_imports = true 11 | -------------------------------------------------------------------------------- /.taplo.toml: -------------------------------------------------------------------------------- 1 | [schema] 2 | path = "taplo://taplo.toml" 3 | 4 | [formatting] 5 | align_entries = true 6 | indent_entries = false 7 | indent_tables = true 8 | reorder_arrays = true 9 | reorder_inline_tables = true 10 | reorder_keys = true 11 | -------------------------------------------------------------------------------- /.yamlfix.toml: -------------------------------------------------------------------------------- 1 | #:schema https://json.schemastore.org/any.json 2 | line_length = 100 3 | section_whitelines = 1 4 | sequence_style = "block_style" 5 | whitelines = 1 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | 6 | 7 | 8 | 9 | ## [0.6.0] _2025-05-07_ 10 | 11 | ### 🚀 Features 12 | 13 | - [**breaking**] Fix lints in level_generator 14 | - [**breaking**] Upgrade to rust edition 2024 15 | - [**breaking**] Refactor level generator and geometric 16 | - _(geometric)_ Add MaxTooLarge error 17 | 18 | ### 🐛 Bug Fixes 19 | 20 | - Supplement the key field of the returned result 21 | 22 | ### 🎨 Styling 23 | 24 | - Fix clippy warnings 25 | - Run `cargo fmt` 26 | - Re-organise cargo toml and format toml 27 | - Format lib.rs 28 | 29 | ### 📚 Documentation 30 | 31 | - Fix github actions badge 32 | 33 | ### ⚙️ Miscellaneous Tasks 34 | 35 | - Fix skiplist lints 36 | - Delete pages submodule 37 | - Update copyright notice 38 | - Extend lints and formatting 39 | - _(bench)_ Fix lints 40 | - Adjust simicolon lint around blocks 41 | - Add editorconfig 42 | - Add pre-commit 43 | - Insert script for docs generation 44 | - Ignore clippy lints 45 | - Exclude slow tests from miri 46 | - _(ci)_ Add renovate config 47 | - _(ci)_ Remove cargo audit 48 | - _(ci)_ Brand new test workflow 49 | - Add nextest config 50 | - Silence clippy for now 51 | - Add copilot instructions 52 | - Add git cliff configuration 53 | - _(ci)_ Reduce update noise 54 | 55 | ### � Other 56 | 57 | - Replace outdated github actions 58 | - Pass extra args to tarpaulin 59 | 60 | ### Contributors 61 | 62 | - @JP-Ellis 63 | - @ByteAlex 64 | - @Aurora2500 65 | - @KKMaaaN 66 | 67 | ## [0.5.0] _2023-03-31_ 68 | 69 | ### 🚀 Features 70 | 71 | - Add Bound api 72 | 73 | ### � Other 74 | 75 | - V0.5.0 76 | 77 | ### Contributors 78 | 79 | - @JP-Ellis 80 | - @KKMaaaN 81 | 82 | ## [0.4.0] _2021-06-28_ 83 | 84 | ### � Other 85 | 86 | - :range() done. 87 | 88 | ### Contributors 89 | 90 | - @JP-Ellis 91 | - @bstrie 92 | - @jovenlin0527 93 | 94 | 95 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | #:schema https://json.schemastore.org/cargo.json 2 | [package] 3 | name = "skiplist" 4 | version = "0.6.0" 5 | 6 | authors = ["JP-Ellis "] 7 | description = "Skiplist implementation in Rust for fast insertion and removal, including a normal skiplist, ordered skiplist, and skipmap." 8 | license = "MIT" 9 | readme = "README.md" 10 | 11 | categories = ["data-structures"] 12 | keywords = ["collection", "skiplist", "sorted"] 13 | 14 | documentation = "https://docs.rs/skiplist" 15 | homepage = "https://jpellis.me/projects/rust-skiplist/" 16 | repository = "https://github.com/JP-Ellis/rust-skiplist/" 17 | 18 | ################################################################################ 19 | ## Dependencies 20 | ################################################################################ 21 | 22 | edition = "2024" 23 | rust-version = "1.85" 24 | 25 | [dependencies] 26 | rand = { version = "0.9", features = ["small_rng"] } 27 | thiserror = "2.0" 28 | 29 | [dev-dependencies] 30 | anyhow = "=1.0.98" 31 | criterion = "=0.3.6" 32 | insta = "=1.43.1" 33 | pretty_assertions = "=1.4.1" 34 | rstest = "=0.25.0" 35 | 36 | ################################################################################ 37 | ## Metadata 38 | ################################################################################ 39 | 40 | [package.metadata] 41 | 42 | [package.metadata.docs.rs] 43 | rustdoc-args = [ 44 | "--html-in-header", 45 | ".cargo/katex.header.html", 46 | 47 | "--html-after-content", 48 | ".cargo/katex.script.html", 49 | 50 | "--html-in-header", 51 | ".cargo/mermaid.header.html", 52 | 53 | "--html-after-content", 54 | ".cargo/mermaid.script.html", 55 | ] 56 | 57 | ################################################################################ 58 | ## Lints 59 | ################################################################################ 60 | [lints] 61 | 62 | [lints.rust] 63 | future-incompatible = "warn" 64 | missing_docs = "warn" 65 | warnings = "warn" 66 | 67 | [lints.clippy] 68 | # Lower the priority of groups to allow overriding individual lints 69 | cargo = { level = "warn", priority = -1 } 70 | complexity = { level = "warn", priority = -1 } 71 | correctness = { level = "warn", priority = -1 } 72 | pedantic = { level = "warn", priority = -1 } 73 | perf = { level = "warn", priority = -1 } 74 | restriction = { level = "warn", priority = -1 } 75 | style = { level = "warn", priority = -1 } 76 | suspicious = { level = "warn", priority = -1 } 77 | 78 | ######################################## 79 | # Restriction Lints 80 | ######################################## 81 | # The restriction group contains lints which Clippy deems as opt-in. I prefer 82 | # using an opt-out approach. 83 | blanket-clippy-restriction-lints = "allow" 84 | 85 | arbitrary_source_item_ordering = "allow" 86 | default_numeric_fallback = "allow" 87 | else_if_without_else = "allow" 88 | impl_trait_in_params = "allow" 89 | implicit_return = "allow" 90 | min_ident_chars = "allow" 91 | missing_trait_methods = "allow" 92 | pattern_type_mismatch = "allow" 93 | pub_with_shorthand = "allow" 94 | question_mark_used = "allow" 95 | ref_patterns = "allow" 96 | self_named_module_files = "allow" 97 | separated_literal_suffix = "allow" 98 | similar_names = "allow" 99 | single_call_fn = "allow" 100 | single_char_lifetime_names = "allow" 101 | std_instead_of_alloc = "allow" 102 | std_instead_of_core = "allow" 103 | string_add = "allow" 104 | unreachable = "allow" 105 | unused_trait_names = "allow" 106 | 107 | # TODO: Revisit this once https://github.com/rust-lang/rust-clippy/issues/14056 108 | # is resolved. 109 | panic_in_result_fn = "allow" 110 | 111 | [lints.rustdoc] 112 | missing-crate-level-docs = "warn" 113 | 114 | [badges] 115 | codecov = { repository = "JP-Ellis/rust-skiplist", branch = "master", service = "github" } 116 | 117 | [[bench]] 118 | harness = false 119 | name = "main" 120 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2025 Joshua Ellis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Rust Skiplist](http://www.jpellis.me/projects/rust-skiplist) 2 | 3 | [![crates.io](https://img.shields.io/crates/v/skiplist.svg)](https://crates.io/crates/skiplist) 4 | [![crates.io](https://img.shields.io/crates/d/skiplist.svg)](https://crates.io/crates/skiplist) 5 | [![Codecov branch](https://img.shields.io/codecov/c/github/JP-Ellis/rust-skiplist/master)](https://codecov.io/gh/JP-Ellis/rust-skiplist) 6 | [![Build Status](https://img.shields.io/github/actions/workflow/status/JP-Ellis/rust-skiplist/rust.yml?branch=master)](https://github.com/JP-Ellis/rust-skiplist/actions) 7 | 8 | A [skiplist](http://en.wikipedia.org/wiki/Skip_list) provides a way of storing 9 | data with `log(i)` access, insertion and removal for an element in the `i`th 10 | position. 11 | 12 | There are three kinds of collections defined here: 13 | 14 | - **SkipList** This behaves like nearly any other double-ended list. 15 | - **OrderedSkipList** Ensures that the elements are always sorted. Still allows 16 | for access nodes at a given index. 17 | - **SkipMap** A map in which the keys are ordered. 18 | 19 | Documentation can be found on [docs.rs](https://docs.rs/skiplist) and the cargo 20 | crate can be found on [crates.io](https://crates.io/crates/skiplist). 21 | -------------------------------------------------------------------------------- /benches/btreemap.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarks for the Standard Library's [`BTreeMap`]. 2 | 3 | use std::collections::BTreeMap; 4 | 5 | use criterion::{AxisScale, BenchmarkId, Criterion, PlotConfiguration, black_box}; 6 | use rand::{Rng, SeedableRng, rngs::StdRng}; 7 | 8 | /// Benchmarking sizes 9 | const SIZES: [usize; 6] = [1, 10, 100, 1000, 10_000, 100_000]; 10 | 11 | /// Benchmarking insertion 12 | pub fn insert(c: &mut Criterion) { 13 | let mut group = c.benchmark_group("BTreeMap Insert"); 14 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 15 | 16 | for size in SIZES { 17 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 18 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 19 | let mut sl: BTreeMap = 20 | std::iter::repeat_with(|| rng.random()).take(size).collect(); 21 | 22 | b.iter(|| { 23 | sl.insert(rng.random(), rng.random()); 24 | }); 25 | }); 26 | } 27 | } 28 | 29 | /// Benchmarking random access 30 | pub fn rand_access(c: &mut Criterion) { 31 | let mut group = c.benchmark_group("BTreeMap Random Access"); 32 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 33 | 34 | for size in SIZES { 35 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 36 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 37 | let sl: BTreeMap = std::iter::repeat_with(|| rng.random()) 38 | .enumerate() 39 | .take(size) 40 | .collect(); 41 | let indices: Vec = std::iter::repeat_with(|| rng.random_range(0..sl.len())) 42 | .take(10) 43 | .collect(); 44 | 45 | b.iter(|| { 46 | for i in &indices { 47 | black_box(sl.get(i)); 48 | } 49 | }); 50 | }); 51 | } 52 | } 53 | 54 | /// Benchmarking iteration 55 | pub fn iter(c: &mut Criterion) { 56 | c.bench_function("BTreeMap Iter", |b| { 57 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 58 | let sl: BTreeMap = std::iter::repeat_with(|| rng.random()) 59 | .take(100_000) 60 | .collect(); 61 | 62 | b.iter(|| { 63 | for el in &sl { 64 | black_box(el); 65 | } 66 | }); 67 | }); 68 | } 69 | -------------------------------------------------------------------------------- /benches/hashmap.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarks for the Standard Library's [`HashMap`]. 2 | 3 | use std::collections::HashMap; 4 | 5 | use criterion::{AxisScale, BenchmarkId, Criterion, PlotConfiguration, black_box}; 6 | use rand::{Rng, SeedableRng, rngs::StdRng}; 7 | 8 | /// Benchmarking sizes 9 | const SIZES: [usize; 6] = [1, 10, 100, 1000, 10_000, 100_000]; 10 | 11 | /// Benchmarking insertion 12 | pub fn insert(c: &mut Criterion) { 13 | let mut group = c.benchmark_group("HashMap Insert"); 14 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 15 | 16 | for size in SIZES { 17 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 18 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 19 | let mut sl: HashMap = 20 | std::iter::repeat_with(|| rng.random()).take(size).collect(); 21 | 22 | b.iter(|| { 23 | sl.insert(rng.random(), rng.random()); 24 | }); 25 | }); 26 | } 27 | } 28 | 29 | /// Benchmarking random access 30 | pub fn rand_access(c: &mut Criterion) { 31 | let mut group = c.benchmark_group("HashMap Random Access"); 32 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 33 | 34 | for size in SIZES { 35 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 36 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 37 | let sl: HashMap = std::iter::repeat_with(|| rng.random()) 38 | .enumerate() 39 | .take(size) 40 | .collect(); 41 | let indices: Vec = std::iter::repeat_with(|| rng.random_range(0..sl.len())) 42 | .take(10) 43 | .collect(); 44 | 45 | b.iter(|| { 46 | for i in &indices { 47 | black_box(sl.get(i)); 48 | } 49 | }); 50 | }); 51 | } 52 | } 53 | 54 | /// Benchmarking iteration 55 | pub fn iter(c: &mut Criterion) { 56 | c.bench_function("HashMap Iter", |b| { 57 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 58 | let sl: HashMap = std::iter::repeat_with(|| rng.random()) 59 | .take(100_000) 60 | .collect(); 61 | 62 | b.iter(|| { 63 | #[expect(clippy::iter_over_hash_type, reason = "for benchmarking")] 64 | for el in &sl { 65 | black_box(el); 66 | } 67 | }); 68 | }); 69 | } 70 | -------------------------------------------------------------------------------- /benches/linkedlist.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarks for the Standard Library's [`LinkedList`]. 2 | 3 | use std::collections::LinkedList; 4 | 5 | use criterion::{AxisScale, BenchmarkId, Criterion, PlotConfiguration, black_box}; 6 | use rand::{Rng, SeedableRng, rngs::StdRng}; 7 | 8 | /// Benchmarking sizes 9 | const SIZES: [usize; 6] = [1, 10, 100, 1000, 10_000, 100_000]; 10 | 11 | /// Benchmarking push front 12 | pub fn push_front(c: &mut Criterion) { 13 | let mut group = c.benchmark_group("LinkedList Push Front"); 14 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 15 | 16 | for size in SIZES { 17 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 18 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 19 | let mut sl: LinkedList = 20 | std::iter::repeat_with(|| rng.random()).take(size).collect(); 21 | 22 | b.iter(|| { 23 | sl.push_front(rng.random()); 24 | }); 25 | }); 26 | } 27 | } 28 | 29 | /// Benchmarking push back 30 | pub fn push_back(c: &mut Criterion) { 31 | let mut group = c.benchmark_group("LinkedList Push Back"); 32 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 33 | 34 | for size in SIZES { 35 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 36 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 37 | let mut sl: LinkedList = 38 | std::iter::repeat_with(|| rng.random()).take(size).collect(); 39 | 40 | b.iter(|| { 41 | sl.push_back(rng.random()); 42 | }); 43 | }); 44 | } 45 | } 46 | 47 | /// Benchmarking random access 48 | pub fn rand_access(c: &mut Criterion) { 49 | let mut group = c.benchmark_group("LinkedList Random Access"); 50 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 51 | 52 | for size in SIZES { 53 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 54 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 55 | let sl: LinkedList = std::iter::repeat_with(|| rng.random()).take(size).collect(); 56 | let indices: Vec<_> = std::iter::repeat_with(|| rng.random_range(0..sl.len())) 57 | .take(10) 58 | .collect(); 59 | 60 | b.iter(|| { 61 | for &i in &indices { 62 | if let Some(el) = sl.iter().nth(i) { 63 | black_box(el); 64 | } 65 | } 66 | }); 67 | }); 68 | } 69 | } 70 | 71 | /// Benchmarking iteration 72 | pub fn iter(c: &mut Criterion) { 73 | c.bench_function("LinkedList Iter", |b| { 74 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 75 | let sl: LinkedList = std::iter::repeat_with(|| rng.random()) 76 | .take(100_000) 77 | .collect(); 78 | 79 | b.iter(|| { 80 | for el in &sl { 81 | black_box(el); 82 | } 83 | }); 84 | }); 85 | } 86 | -------------------------------------------------------------------------------- /benches/main.rs: -------------------------------------------------------------------------------- 1 | //! # Benchmarks 2 | //! 3 | //! Setup and configure benchmarks to compare the performance of our skiplist 4 | //! implementation against the standard library's data structures. 5 | 6 | #![expect( 7 | missing_docs, 8 | reason = "benchmarking and criterion creates boilerplate" 9 | )] 10 | 11 | use criterion::{Criterion, criterion_group, criterion_main}; 12 | 13 | mod btreemap; 14 | mod hashmap; 15 | mod linkedlist; 16 | mod ordered_skiplist; 17 | mod skiplist; 18 | mod skipmap; 19 | mod vec; 20 | mod vecdeque; 21 | 22 | criterion_group!( 23 | name = benches; 24 | config = Criterion::default(); 25 | targets = 26 | crate::btreemap::insert, 27 | crate::btreemap::iter, 28 | crate::btreemap::rand_access, 29 | crate::hashmap::insert, 30 | crate::hashmap::iter, 31 | crate::hashmap::rand_access, 32 | crate::linkedlist::iter, 33 | crate::linkedlist::push_back, 34 | crate::linkedlist::push_front, 35 | crate::linkedlist::rand_access, 36 | crate::ordered_skiplist::insert, 37 | crate::ordered_skiplist::iter, 38 | crate::ordered_skiplist::rand_access, 39 | crate::skiplist::iter, 40 | crate::skiplist::push_back, 41 | crate::skiplist::push_front, 42 | crate::skiplist::rand_access, 43 | crate::skipmap::insert, 44 | crate::skipmap::iter, 45 | crate::skipmap::rand_access, 46 | crate::vec::insert, 47 | crate::vec::iter, 48 | crate::vec::push_back, 49 | crate::vec::push_front, 50 | crate::vec::rand_access, 51 | crate::vecdeque::iter, 52 | crate::vecdeque::push_back, 53 | crate::vecdeque::push_front, 54 | crate::vecdeque::rand_access, 55 | ); 56 | 57 | criterion_main!(benches); 58 | -------------------------------------------------------------------------------- /benches/ordered_skiplist.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarks for this crate's [`OrderedSkipList`]. 2 | 3 | use criterion::{AxisScale, BenchmarkId, Criterion, PlotConfiguration, black_box}; 4 | use rand::{Rng, SeedableRng, rngs::StdRng}; 5 | use skiplist::OrderedSkipList; 6 | 7 | /// Benchmarking sizes 8 | const SIZES: [usize; 6] = [1, 10, 100, 1000, 10_000, 100_000]; 9 | 10 | /// Benchmarking insertion 11 | pub fn insert(c: &mut Criterion) { 12 | let mut group = c.benchmark_group("OrderedSkiplist Insert"); 13 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 14 | 15 | for size in SIZES { 16 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 17 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 18 | let mut sl: OrderedSkipList = 19 | std::iter::repeat_with(|| rng.random()).take(size).collect(); 20 | 21 | b.iter(|| { 22 | sl.insert(rng.random()); 23 | }); 24 | }); 25 | } 26 | } 27 | 28 | /// Benchmarking random access 29 | pub fn rand_access(c: &mut Criterion) { 30 | let mut group = c.benchmark_group("OrderedSkiplist Random Access"); 31 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 32 | 33 | for size in SIZES { 34 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 35 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 36 | let sl: OrderedSkipList = 37 | std::iter::repeat_with(|| rng.random()).take(size).collect(); 38 | let indices: Vec<_> = std::iter::repeat_with(|| rng.random_range(0..sl.len())) 39 | .take(10) 40 | .collect(); 41 | 42 | b.iter(|| { 43 | for &i in &indices { 44 | black_box(sl.get(i)); 45 | } 46 | }); 47 | }); 48 | } 49 | } 50 | 51 | /// Benchmarking iteration 52 | pub fn iter(c: &mut Criterion) { 53 | c.bench_function("OrderedSkipList Iter", |b| { 54 | let mut rng = StdRng::seed_from_u64(0x123_4abcd); 55 | let sl: OrderedSkipList = std::iter::repeat_with(|| rng.random()) 56 | .take(100_000) 57 | .collect(); 58 | 59 | b.iter(|| { 60 | for el in &sl { 61 | black_box(el); 62 | } 63 | }); 64 | }); 65 | } 66 | -------------------------------------------------------------------------------- /benches/skiplist.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarks for this crate's [`SkipList`]. 2 | 3 | use criterion::{AxisScale, BenchmarkId, Criterion, PlotConfiguration, black_box}; 4 | use rand::{Rng, SeedableRng, rngs::StdRng}; 5 | use skiplist::SkipList; 6 | 7 | /// Benchmarking sizes 8 | const SIZES: [usize; 6] = [1, 10, 100, 1000, 10_000, 100_000]; 9 | 10 | /// Benchmarking push front 11 | pub fn push_front(c: &mut Criterion) { 12 | let mut group = c.benchmark_group("SkipList Push Front"); 13 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 14 | 15 | for size in SIZES { 16 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 17 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 18 | let mut sl: SkipList = 19 | std::iter::repeat_with(|| rng.random()).take(size).collect(); 20 | 21 | b.iter(|| { 22 | sl.push_front(rng.random()); 23 | }); 24 | }); 25 | } 26 | } 27 | 28 | /// Benchmarking push back 29 | pub fn push_back(c: &mut Criterion) { 30 | let mut group = c.benchmark_group("SkipList Push Back"); 31 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 32 | 33 | for size in SIZES { 34 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 35 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 36 | let mut sl: SkipList = 37 | std::iter::repeat_with(|| rng.random()).take(size).collect(); 38 | 39 | b.iter(|| { 40 | sl.push_back(rng.random()); 41 | }); 42 | }); 43 | } 44 | } 45 | 46 | /// Benchmarking random access 47 | pub fn rand_access(c: &mut Criterion) { 48 | let mut group = c.benchmark_group("SkipList Random Access"); 49 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 50 | 51 | for size in SIZES { 52 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 53 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 54 | let sl: SkipList = std::iter::repeat_with(|| rng.random()).take(size).collect(); 55 | let indices: Vec<_> = std::iter::repeat_with(|| rng.random_range(0..sl.len())) 56 | .take(10) 57 | .collect(); 58 | 59 | b.iter(|| { 60 | for &i in &indices { 61 | black_box(sl.get(i)); 62 | } 63 | }); 64 | }); 65 | } 66 | } 67 | 68 | /// Benchmarking iteration 69 | pub fn iter(c: &mut Criterion) { 70 | c.bench_function("SkipList Iter", |b| { 71 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 72 | let sl: SkipList = std::iter::repeat_with(|| rng.random()) 73 | .take(100_000) 74 | .collect(); 75 | 76 | b.iter(|| { 77 | for el in &sl { 78 | black_box(el); 79 | } 80 | }); 81 | }); 82 | } 83 | -------------------------------------------------------------------------------- /benches/skipmap.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarks for this crate's [`SkipMap`]. 2 | 3 | use criterion::{AxisScale, BenchmarkId, Criterion, PlotConfiguration, black_box}; 4 | use rand::{Rng, SeedableRng, rngs::StdRng}; 5 | use skiplist::SkipMap; 6 | 7 | /// Benchmarking sizes 8 | const SIZES: [usize; 6] = [1, 10, 100, 1000, 10_000, 100_000]; 9 | 10 | /// Benchmarking insertion 11 | pub fn insert(c: &mut Criterion) { 12 | let mut group = c.benchmark_group("SkipMap Insert"); 13 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 14 | 15 | for size in SIZES { 16 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 17 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 18 | let mut sl: SkipMap = 19 | std::iter::repeat_with(|| rng.random()).take(size).collect(); 20 | 21 | b.iter(|| { 22 | sl.insert(rng.random(), rng.random()); 23 | }); 24 | }); 25 | } 26 | } 27 | 28 | /// Benchmarking random access 29 | pub fn rand_access(c: &mut Criterion) { 30 | let mut group = c.benchmark_group("SkipMap Random Access"); 31 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 32 | 33 | for size in SIZES { 34 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 35 | let mut rng = StdRng::seed_from_u64(0x123_4abcd); 36 | let sl: SkipMap = std::iter::repeat_with(|| rng.random()) 37 | .enumerate() 38 | .take(size) 39 | .collect(); 40 | let indices: Vec = std::iter::repeat_with(|| rng.random_range(0..sl.len())) 41 | .take(10) 42 | .collect(); 43 | 44 | b.iter(|| { 45 | for i in &indices { 46 | black_box(sl.get(i)); 47 | } 48 | }); 49 | }); 50 | } 51 | } 52 | 53 | /// Benchmarking iteration 54 | pub fn iter(c: &mut Criterion) { 55 | c.bench_function("SkipMap Iter", |b| { 56 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 57 | let sl: SkipMap = std::iter::repeat_with(|| rng.random()) 58 | .take(100_000) 59 | .collect(); 60 | 61 | b.iter(|| { 62 | for el in &sl { 63 | black_box(el); 64 | } 65 | }); 66 | }); 67 | } 68 | -------------------------------------------------------------------------------- /benches/vec.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarks for the Standard Library's [`Vec`]. 2 | 3 | use criterion::{AxisScale, BenchmarkId, Criterion, PlotConfiguration, black_box}; 4 | use rand::{Rng, SeedableRng, rngs::StdRng}; 5 | 6 | /// Benchmarking sizes 7 | const SIZES: [usize; 6] = [1, 10, 100, 1000, 10_000, 100_000]; 8 | 9 | /// Benchmarking push front 10 | pub fn push_front(c: &mut Criterion) { 11 | let mut group = c.benchmark_group("Vec Push Front"); 12 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 13 | 14 | for size in SIZES { 15 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 16 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 17 | let mut sl: Vec = std::iter::repeat_with(|| rng.random()).take(size).collect(); 18 | 19 | b.iter(|| { 20 | sl.insert(0, rng.random()); 21 | }); 22 | }); 23 | } 24 | } 25 | 26 | /// Benchmarking push back 27 | pub fn push_back(c: &mut Criterion) { 28 | let mut group = c.benchmark_group("Vec Push Back"); 29 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 30 | 31 | for size in SIZES { 32 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 33 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 34 | let mut sl: Vec = std::iter::repeat_with(|| rng.random()).take(size).collect(); 35 | 36 | b.iter(|| { 37 | sl.push(rng.random()); 38 | }); 39 | }); 40 | } 41 | } 42 | 43 | /// Benchmarking insertion 44 | pub fn insert(c: &mut Criterion) { 45 | let mut group = c.benchmark_group("Vec Insert"); 46 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 47 | 48 | for size in SIZES { 49 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 50 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 51 | let mut sl: Vec = std::iter::repeat_with(|| rng.random()).take(size).collect(); 52 | 53 | b.iter(|| { 54 | let v = rng.random(); 55 | match sl.binary_search(&v) { 56 | Ok(i) | Err(i) => sl.insert(i, v), 57 | } 58 | }); 59 | }); 60 | } 61 | } 62 | 63 | /// Benchmarking random access 64 | pub fn rand_access(c: &mut Criterion) { 65 | let mut group = c.benchmark_group("Vec Random Access"); 66 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 67 | 68 | for size in SIZES { 69 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 70 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 71 | let sl: Vec = std::iter::repeat_with(|| rng.random()).take(size).collect(); 72 | let indices: Vec<_> = std::iter::repeat_with(|| rng.random_range(0..sl.len())) 73 | .take(10) 74 | .collect(); 75 | 76 | b.iter(|| { 77 | for &i in &indices { 78 | black_box(sl.get(i)); 79 | } 80 | }); 81 | }); 82 | } 83 | } 84 | 85 | /// Benchmarking iteration 86 | pub fn iter(c: &mut Criterion) { 87 | c.bench_function("Vec Iter", |b| { 88 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 89 | let sl: Vec = std::iter::repeat_with(|| rng.random()) 90 | .take(100_000) 91 | .collect(); 92 | 93 | b.iter(|| { 94 | for el in &sl { 95 | black_box(el); 96 | } 97 | }); 98 | }); 99 | } 100 | -------------------------------------------------------------------------------- /benches/vecdeque.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarks for the Standard Library's [`VecDeque`]. 2 | 3 | use std::collections::VecDeque; 4 | 5 | use criterion::{AxisScale, BenchmarkId, Criterion, PlotConfiguration, black_box}; 6 | use rand::{Rng, SeedableRng, rngs::StdRng}; 7 | 8 | /// Benchmarking sizes 9 | const SIZES: [usize; 6] = [1, 10, 100, 1000, 10_000, 100_000]; 10 | 11 | /// Benchmarking push front 12 | pub fn push_front(c: &mut Criterion) { 13 | let mut group = c.benchmark_group("VecDeque Push Front"); 14 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 15 | 16 | for size in SIZES { 17 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 18 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 19 | let mut sl: VecDeque = 20 | std::iter::repeat_with(|| rng.random()).take(size).collect(); 21 | 22 | b.iter(|| { 23 | sl.push_front(rng.random()); 24 | }); 25 | }); 26 | } 27 | } 28 | 29 | /// Benchmarking push back 30 | pub fn push_back(c: &mut Criterion) { 31 | let mut group = c.benchmark_group("VecDeque Push Back"); 32 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 33 | 34 | for size in SIZES { 35 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 36 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 37 | let mut sl: VecDeque = 38 | std::iter::repeat_with(|| rng.random()).take(size).collect(); 39 | 40 | b.iter(|| { 41 | sl.push_back(rng.random()); 42 | }); 43 | }); 44 | } 45 | } 46 | 47 | /// Benchmarking random access 48 | pub fn rand_access(c: &mut Criterion) { 49 | let mut group = c.benchmark_group("VecDeque Random Access"); 50 | group.plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic)); 51 | 52 | for size in SIZES { 53 | group.bench_function(BenchmarkId::from_parameter(size), |b| { 54 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 55 | let sl: VecDeque = std::iter::repeat_with(|| rng.random()).take(size).collect(); 56 | let indices: Vec<_> = std::iter::repeat_with(|| rng.random_range(0..sl.len())) 57 | .take(10) 58 | .collect(); 59 | 60 | b.iter(|| { 61 | for &i in &indices { 62 | black_box(sl.get(i)); 63 | } 64 | }); 65 | }); 66 | } 67 | } 68 | 69 | /// Benchmarking iteration 70 | pub fn iter(c: &mut Criterion) { 71 | c.bench_function("VecDeque Iter", |b| { 72 | let mut rng = StdRng::seed_from_u64(0x1234_abcd); 73 | let sl: VecDeque = std::iter::repeat_with(|| rng.random()) 74 | .take(100_000) 75 | .collect(); 76 | 77 | b.iter(|| { 78 | for el in &sl { 79 | black_box(el); 80 | } 81 | }); 82 | }); 83 | } 84 | -------------------------------------------------------------------------------- /cliff.toml: -------------------------------------------------------------------------------- 1 | #:schema https://json.schemastore.org/any.json 2 | # git-cliff ~ default configuration file 3 | # https://git-cliff.org/docs/configuration 4 | 5 | [changelog] 6 | # template for the changelog header 7 | header = """ 8 | # Changelog\n 9 | All notable changes to this project will be documented in this file. 10 | 11 | 12 | 13 | 14 | 15 | """ 16 | # template for the changelog body 17 | # https://keats.github.io/tera/docs/#introduction 18 | body = """ 19 | {% if version -%} 20 | ## [{{ version | trim_start_matches(pat="v") }}] _{{ timestamp | date(format="%Y-%m-%d") }}_ 21 | 22 | {% else -%} 23 | ## [unreleased] 24 | 25 | {% endif -%} 26 | {% for group, commits in commits | group_by(attribute="group") -%} 27 | ### {{ group | striptags | trim | upper_first }} 28 | 29 | {% for commit in commits -%} 30 | - {% if commit.scope %}_({{ commit.scope }})_ {% endif -%} 31 | {% if commit.breaking %}[**breaking**] {% endif -%} 32 | {{ commit.message | upper_first }} 33 | {% endfor %} 34 | {% endfor -%} 35 | {% if github.contributors -%}\ 36 | ### Contributors 37 | 38 | {% for contributor in github.contributors -%}\ 39 | {% if not contributor.username or contributor.username is ending_with("[bot]") %}{% continue %}{% endif -%} 40 | - @{{ contributor.username }} 41 | {% endfor %} 42 | {% endif -%} 43 | 44 | """ 45 | # template for the changelog footer 46 | footer = """ 47 | 48 | """ 49 | # remove the leading and trailing s 50 | trim = true 51 | # postprocessors 52 | postprocessors = [] 53 | # render body even when there are no releases to process 54 | # render_always = true 55 | # output file path 56 | output = "CHANGELOG.md" 57 | 58 | [git] 59 | # parse the commits based on https://www.conventionalcommits.org 60 | conventional_commits = true 61 | # filter out the commits that are not conventional 62 | filter_unconventional = true 63 | # process each line of a commit as an individual commit 64 | split_commits = false 65 | # regex for preprocessing the commit messages 66 | commit_preprocessors = [ 67 | # Remove the PR number added by GitHub when merging PR in UI 68 | { pattern = '\s*\(#([0-9]+)\)$', replace = "" }, 69 | # Check spelling of the commit with https://github.com/crate-ci/typos 70 | { pattern = '.*', replace_command = 'typos --write-changes -' }, 71 | ] 72 | # regex for parsing and grouping commits 73 | commit_parsers = [ 74 | # Ignore deps commits from the changelog 75 | { message = "^(chore|fix)\\(deps.*\\)", skip = true }, 76 | # Group commits by type 77 | { group = "🚀 Features", message = "^feat" }, 78 | { group = "🐛 Bug Fixes", message = "^fix" }, 79 | { group = "🚜 Refactor", message = "^refactor" }, 80 | { group = "⚡ Performance", message = "^perf" }, 81 | { group = "🎨 Styling", message = "^style" }, 82 | { group = "📚 Documentation", message = "^docs" }, 83 | { group = "🧪 Testing", message = "^test" }, 84 | { group = "◀️ Revert", message = "^revert" }, 85 | { group = "⚙️ Miscellaneous Tasks", message = "^chore" }, 86 | { group = "� Other", message = ".*" }, 87 | ] 88 | # filter out the commits that are not matched by commit parsers 89 | filter_commits = false 90 | # sort the tags topologically 91 | topo_order = false 92 | # sort the commits inside sections by oldest/newest order 93 | sort_commits = "oldest" 94 | 95 | [remote.github] 96 | owner = "JP-Ellis" 97 | repo = "rust-skiplist" 98 | -------------------------------------------------------------------------------- /committed.toml: -------------------------------------------------------------------------------- 1 | #:schema https://raw.githubusercontent.com/crate-ci/committed/refs/heads/master/config.schema.json 2 | ## Configuration for committed 3 | ## 4 | ## See 5 | style = "conventional" 6 | 7 | line_length = 80 8 | merge_commit = false 9 | no_fixup = false 10 | subject_capitalized = false 11 | 12 | allowed_types = [ 13 | "chore", 14 | "docs", 15 | "feat", 16 | "fix", 17 | "perf", 18 | "refactor", 19 | "revert", 20 | "style", 21 | "test", 22 | ] 23 | 24 | # The author string is of the form `Name `. We want to ignore all bots 25 | # which typically have are ofthe form `some-name[bot] `. 26 | ignore_author_re = "(?i)^.*\\[bot\\] <.*>$" 27 | -------------------------------------------------------------------------------- /criterion.toml: -------------------------------------------------------------------------------- 1 | #:schema https://json.schemastore.org/any.json 2 | criterion_home = "./criterion" 3 | plotting_backend = "plotters" 4 | -------------------------------------------------------------------------------- /src/level_generator.rs: -------------------------------------------------------------------------------- 1 | //! Level generator for skip lists. 2 | //! 3 | //! Skiplists use a probabilistic distribution to determine the links. If 4 | //! `$S^{(0)} = \{n_1, n_2, \dots, n_p\}$` is the set of all the nodes, then 5 | //! `$S^{(i)} \subseteq S^{(i-1)}$` is the set of nodes at level `$i$`. 6 | //! 7 | //! At each level, the nodes are linked to the next node in the list, and 8 | //! therefore as the level increase and the number of nodes decrease, it is 9 | //! expected that the distance between the nodes also increases making the 10 | //! traversal of the list faster. 11 | //! 12 | //! The simplest way to implement this is to have the same probability `$p$` 13 | //! that a node is present in the next level, with `$p$` being constant. This is 14 | //! known as a geometric distribution and is implemented in 15 | //! [`Geometric`][geometric::Geometric]. 16 | //! 17 | //! It is very unlikely that this will need to be changed as the default should 18 | //! suffice, but if need be custom level generators can be implemented through 19 | //! the [`LevelGenerator`] trait. 20 | 21 | pub mod geometric; 22 | 23 | /// Trait for generating the level of a new node to be inserted into the 24 | /// skiplist. 25 | pub trait LevelGenerator { 26 | /// The total number of levels that are assumed to exist. 27 | #[must_use] 28 | fn total(&self) -> usize; 29 | /// Generate a random level for a new node in the range `[0, total)`. 30 | /// 31 | /// This function must not return a level greater or equal to 32 | /// [`total`][LevelGenerator::total]. 33 | #[must_use] 34 | fn level(&mut self) -> usize; 35 | } 36 | -------------------------------------------------------------------------------- /src/level_generator/geometric.rs: -------------------------------------------------------------------------------- 1 | //! Geometric level generator. 2 | 3 | use rand::{Rng, SeedableRng, rngs::SmallRng}; 4 | use thiserror::Error; 5 | 6 | use crate::level_generator::LevelGenerator; 7 | 8 | #[derive(Error, Debug, PartialEq, Eq)] 9 | /// Errors that can occur when creating a [`Geometric`] level generator. 10 | #[expect( 11 | clippy::module_name_repetitions, 12 | reason = "Using 'Error' would be too generic and may cause confusion." 13 | )] 14 | #[non_exhaustive] 15 | pub enum GeometricError { 16 | /// The maximum number of levels must be non-zero. 17 | #[error("max must be non-zero.")] 18 | ZeroMax, 19 | /// The maximum number of levels must be less than `i32::MAX`. 20 | #[error("max must be less than i32::MAX.")] 21 | MaxTooLarge, 22 | /// The probability `$p$` must be in the range `$(0, 1)$`. 23 | #[error("p must be in (0, 1).")] 24 | InvalidProbability, 25 | /// Failed to initialize the random number generator. 26 | #[error("Failed to initialize the random number generator.")] 27 | RngInitFailed, 28 | } 29 | 30 | /// A level generator using a geometric distribution. 31 | /// 32 | /// This distribution assumes that if a node is present at some level `$n$`, 33 | /// then the probability that it is present at level `$n+1$` is some constant 34 | /// `$p \in (0, 1)$`. This produces a geometric distribution, albeit truncated 35 | /// at the maximum number of levels allowed. 36 | #[derive(Debug)] 37 | pub struct Geometric { 38 | /// The total number of levels that are assumed to exist. 39 | total: usize, 40 | /// The probability that a node is not present in the next level. 41 | /// 42 | /// While the geometric distribution is defined using the probability `$p$`, 43 | /// the computations needed rely on `$q = 1 - p$`. 44 | q: f64, 45 | /// The random number generator. 46 | rng: SmallRng, 47 | } 48 | impl Geometric { 49 | /// Create a new geometric level generator with `total` number of levels, 50 | /// and `p` as the probability that a given node is present in the next 51 | /// level. 52 | /// 53 | /// # Errors 54 | /// 55 | /// `p` must be between 0 and 1 and will panic otherwise. Similarly, `total` 56 | /// must be at greater or equal to 1. 57 | #[inline] 58 | pub fn new(total: usize, p: f64) -> Result { 59 | if total == 0 { 60 | return Err(GeometricError::ZeroMax); 61 | } 62 | match i32::try_from(total) { 63 | Ok(_) => {} 64 | Err(_) => return Err(GeometricError::MaxTooLarge), 65 | } 66 | if !(0.0 < p && p < 1.0) { 67 | return Err(GeometricError::InvalidProbability); 68 | } 69 | #[expect(clippy::float_arithmetic, reason = "Computing q = 1 - p is fine")] 70 | Ok(Geometric { 71 | total, 72 | q: 1.0 - p, 73 | rng: SmallRng::from_rng(&mut rand::rng()), 74 | }) 75 | } 76 | } 77 | 78 | impl LevelGenerator for Geometric { 79 | #[inline] 80 | fn total(&self) -> usize { 81 | self.total 82 | } 83 | 84 | /// Generate a level for a new node using a geometric distribution. 85 | /// 86 | /// This function generate a random level in the range `$[0, \text{total})$` 87 | /// by sample from a uniform distribution and inverting the cumulative 88 | /// distribution function (CDF) of the truncated geometric distribution. 89 | /// 90 | /// The CDF of the truncated geometric distribution is 91 | /// 92 | /// ```math 93 | /// \text{CDF}(n) = \frac{q^n - 1}{q^{t} - 1} 94 | /// ``` 95 | /// 96 | /// where `$q = 1 - p$` and `$t$` is the total number of levels. Inverting 97 | /// it for `$n$` gives: 98 | /// 99 | /// ```math 100 | /// n = \left\lfloor \log_q\left(1 + (q^{\text{total}} - 1) \cdot u\right) \right\rfloor 101 | /// ``` 102 | /// 103 | /// where `$u \in [0, 1]$` is a uniformly distributed random variate. 104 | #[inline] 105 | #[expect(clippy::float_arithmetic, reason = "Computing inverse CDF")] 106 | #[expect( 107 | clippy::cast_possible_truncation, 108 | clippy::cast_sign_loss, 109 | reason = "CDF domain is [0, total] so the cast is safe" 110 | )] 111 | #[expect( 112 | clippy::expect_used, 113 | reason = "Runtime check guarantees total fits in i32" 114 | )] 115 | #[expect(clippy::as_conversions, reason = "No other way to do this")] 116 | fn level(&mut self) -> usize { 117 | let u = self.rng.random::(); 118 | (1.0 + (self 119 | .q 120 | .powi(i32::try_from(self.total).expect("total is guaranteed to fit in i32")) 121 | - 1.0) 122 | * u) 123 | .log(self.q) 124 | .floor() as usize 125 | } 126 | } 127 | 128 | #[cfg(test)] 129 | mod tests { 130 | use anyhow::{Result, bail}; 131 | use pretty_assertions::assert_eq; 132 | use rstest::rstest; 133 | 134 | use super::{Geometric, LevelGenerator}; 135 | use crate::level_generator::geometric::GeometricError; 136 | 137 | #[test] 138 | fn invalid_max() { 139 | assert_eq!(Geometric::new(0, 0.5).err(), Some(GeometricError::ZeroMax)); 140 | } 141 | 142 | #[test] 143 | fn invalid_p() { 144 | assert_eq!( 145 | Geometric::new(1, 0.0).err(), 146 | Some(GeometricError::InvalidProbability) 147 | ); 148 | assert_eq!( 149 | Geometric::new(1, 1.0).err(), 150 | Some(GeometricError::InvalidProbability) 151 | ); 152 | } 153 | 154 | // Miri is very slow, so we use a much smaller number of iterations, and 155 | // don't check for the presence of min and max level nodes. 156 | #[cfg(miri)] 157 | #[rstest] 158 | fn new_miri( 159 | #[values(1, 2, 128, 1024)] n: usize, 160 | #[values(0.01, 0.1, 0.5, 0.99)] p: f64, 161 | ) -> Result<()> { 162 | const MAX: usize = 10; 163 | 164 | let mut generator = Geometric::new(n, p)?; 165 | assert_eq!(generator.total(), n); 166 | for _ in 0..MAX { 167 | let level = generator.level(); 168 | assert!((0..n).contains(&level)); 169 | } 170 | Ok(()) 171 | } 172 | 173 | #[cfg(not(miri))] 174 | #[rstest] 175 | fn new_small( 176 | #[values(1, 2, 4, 8)] n: usize, 177 | #[values(0.01, 0.1, 0.5, 0.8)] p: f64, 178 | ) -> Result<()> { 179 | const MAX: usize = 10_000_000; 180 | 181 | let mut generator = Geometric::new(n, p)?; 182 | assert_eq!(generator.total(), n); 183 | for _ in 0..1_000 { 184 | let level = generator.level(); 185 | assert!((0..n).contains(&level)); 186 | } 187 | // Make sure that we can produce at least one level-0 node, and one at the 188 | // maximum level. 189 | let mut found = false; 190 | for _ in 0..MAX { 191 | let level = generator.level(); 192 | if level == 0 { 193 | found = true; 194 | break; 195 | } 196 | } 197 | if !found { 198 | bail!("Failed to generate a level-0 node."); 199 | } 200 | 201 | found = false; 202 | for _ in 0..MAX { 203 | let level = generator.level(); 204 | if level == n.checked_sub(1).expect("n is guaranteed to be > 0") { 205 | found = true; 206 | break; 207 | } 208 | } 209 | if !found { 210 | bail!( 211 | "Failed to generate a level-{} node.", 212 | n.checked_sub(1).expect("n is guaranteed to be > 0") 213 | ); 214 | } 215 | 216 | Ok(()) 217 | } 218 | 219 | #[cfg(not(miri))] 220 | #[rstest] 221 | fn new_large(#[values(512, 1024)] n: usize, #[values(0.001, 0.01)] p: f64) -> Result<()> { 222 | const MAX: usize = 10_000_000; 223 | 224 | let mut generator = Geometric::new(n, p)?; 225 | assert_eq!(generator.total(), n); 226 | for _ in 0..1_000 { 227 | let level = generator.level(); 228 | assert!((0..n).contains(&level)); 229 | } 230 | // Make sure that we can produce at least one level-0 node, and one at the 231 | // maximum level. 232 | let mut found = false; 233 | for _ in 0..MAX { 234 | let level = generator.level(); 235 | if level == 0 { 236 | found = true; 237 | break; 238 | } 239 | } 240 | if !found { 241 | bail!("Failed to generate a level-0 node."); 242 | } 243 | 244 | found = false; 245 | for _ in 0..MAX { 246 | let level = generator.level(); 247 | if level == n.checked_sub(1).expect("n is guaranteed to be > 0") { 248 | found = true; 249 | break; 250 | } 251 | } 252 | if !found { 253 | bail!( 254 | "Failed to generate a level-{} node.", 255 | n.checked_sub(1).expect("n is guaranteed to be > 0") 256 | ); 257 | } 258 | 259 | Ok(()) 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A skiplist is a way of storing sorted elements in such a way that they can 2 | //! be accessed, inserted and removed, all in `O(log(n))` on average. 3 | //! 4 | //! Conceptually, a skiplist is arranged as follows: 5 | //! 6 | //! ```text 7 | //! ----------> [2] --------------------------------------------------> [9] ----------> 8 | //! ----------> [2] ------------------------------------[7] ----------> [9] ----------> 9 | //! ----------> [2] ----------> [4] ------------------> [7] ----------> [9] --> [10] -> 10 | //! --> [1] --> [2] --> [3] --> [4] --> [5] --> [6] --> [7] --> [8] --> [9] --> [10] -> 11 | //! ``` 12 | //! 13 | //! Each node contains at the very least a link to the next element in the list 14 | //! (corresponding to the lowest level in the above diagram), but it can 15 | //! randomly contain more links which skip further down the list (the *towers* 16 | //! in the above diagram). This allows for the algorithm to move down the list 17 | //! faster than having to visit every element. 18 | //! 19 | //! Conceptually, the skiplist can be thought of as a stack of linked lists. At 20 | //! the very bottom is the full linked list with every element, and each layer 21 | //! above corresponds to a linked list containing a random subset of the 22 | //! elements from the layer immediately below it. The probability distribution 23 | //! that determines this random subset can be customized, but typically a layer 24 | //! will contain half the nodes from the layer below. 25 | //! 26 | //! # Safety 27 | //! 28 | //! The ordered skiplist relies on a well-behaved comparison function. 29 | //! Specifically, given some ordering function `f(a, b)`, it **must** satisfy 30 | //! the following properties: 31 | //! 32 | //! - Be well defined: `f(a, b)` should always return the same value 33 | //! - Be anti-symmetric: `f(a, b) == Greater` if and only if `f(b, a) == Less`, 34 | //! and `f(a, b) == Equal == f(b, a)`. 35 | //! - By transitive: If `f(a, b) == Greater` and `f(b, c) == Greater` then `f(a, 36 | //! c) == Greater`. 37 | //! 38 | //! **Failure to satisfy these properties can result in unexpected behavior at 39 | //! best, and at worst will cause a segfault, null deref, or some other bad 40 | //! behavior.** 41 | 42 | // In this library, the notion of 'height' of a node refers to how many links a 43 | // node has (as a result, the minimum height is 1). The 'levels' refer to the 44 | // layers in the above diagram, with level 0 being the bottom-most layer, level 45 | // 1 being the one above level 0, etc. 46 | 47 | extern crate rand; 48 | pub mod level_generator; 49 | pub mod ordered_skiplist; 50 | pub mod skiplist; 51 | pub mod skipmap; 52 | mod skipnode; 53 | 54 | #[expect( 55 | clippy::pub_use, 56 | clippy::useless_attribute, 57 | reason = "Temporary silencing clippy" 58 | )] 59 | pub use crate::{ordered_skiplist::OrderedSkipList, skiplist::SkipList, skipmap::SkipMap}; 60 | -------------------------------------------------------------------------------- /src/skiplist.rs: -------------------------------------------------------------------------------- 1 | //! A skiplist implementation which allows faster random access than a standard linked list. 2 | 3 | #![allow(warnings)] 4 | #![allow(clippy)] 5 | #![allow(unknown_lints)] 6 | 7 | use std::{ 8 | cmp, cmp::Ordering, default, fmt, hash, hash::Hash, iter, ops, ops::Bound, ptr::NonNull, 9 | }; 10 | 11 | pub use crate::skipnode::{IntoIter, Iter, IterMut}; 12 | use crate::{ 13 | level_generator::{LevelGenerator, geometric::Geometric}, 14 | skipnode::SkipNode, 15 | }; 16 | 17 | // //////////////////////////////////////////////////////////////////////////// 18 | // SkipList 19 | // //////////////////////////////////////////////////////////////////////////// 20 | 21 | /// SkipList provides a way of storing elements and provides efficient way to 22 | /// access, insert and remove nodes. 23 | /// 24 | /// Unlike a standard linked list, the skiplist can skip ahead when trying to 25 | /// find a particular index. 26 | pub struct SkipList { 27 | // Storage, this is not sorted 28 | head: Box>, 29 | len: usize, 30 | level_generator: Gen, 31 | } 32 | 33 | // /////////////////////////////////////////////// 34 | // Inherent methods 35 | // /////////////////////////////////////////////// 36 | 37 | impl SkipList { 38 | /// Create a new skiplist with the default number of 16 levels. 39 | /// 40 | /// # Examples 41 | /// 42 | /// ``` 43 | /// use skiplist::SkipList; 44 | /// 45 | /// let mut skiplist: SkipList = SkipList::new(); 46 | /// ``` 47 | #[inline] 48 | pub fn new() -> Self { 49 | // Parameters are fixed and will produce a valid level generator. 50 | let lg = Geometric::new(16, 1.0 / 2.0).expect("Failed to create level generator."); 51 | Self::new_from_gen(lg) 52 | } 53 | 54 | /// Constructs a new, empty skiplist with the optimal number of levels for 55 | /// the intended capacity. Specifically, it uses `floor(log2(capacity))` 56 | /// number of levels, ensuring that only *a few* nodes occupy the highest 57 | /// level. 58 | /// 59 | /// # Examples 60 | /// 61 | /// ``` 62 | /// use skiplist::SkipList; 63 | /// 64 | /// let mut skiplist = SkipList::with_capacity(100); 65 | /// skiplist.extend(0..100); 66 | /// ``` 67 | #[inline] 68 | pub fn with_capacity(capacity: usize) -> Self { 69 | let levels = cmp::max(1, (capacity as f64).log2().floor() as usize); 70 | // Parameters are safe as levels >= 1 and p is in (0, 1). 71 | let lg = Geometric::new(levels, 1.0 / 2.0).expect("Failed to create level generator."); 72 | Self::new_from_gen(lg) 73 | } 74 | } 75 | 76 | impl SkipList 77 | where 78 | G: LevelGenerator, 79 | { 80 | /// Constructs a new, empty skip list using a given [LevelGenerator] 81 | /// 82 | /// # Examples 83 | /// 84 | /// ``` 85 | /// use skiplist::{SkipList, level_generator::GeometricalLevelGenerator}; 86 | /// 87 | /// let lg = GeometricalLevelGenerator::new(10, 0.5); 88 | /// let mut skiplist: SkipList = SkipList::new_from_gen(lg); 89 | /// ``` 90 | #[inline] 91 | pub fn new_from_gen(lg: G) -> Self { 92 | SkipList { 93 | head: Box::new(SkipNode::head(lg.total())), 94 | len: 0, 95 | level_generator: lg, 96 | } 97 | } 98 | 99 | /// Clears the skiplist, removing all values. 100 | /// 101 | /// # Examples 102 | /// 103 | /// ``` 104 | /// use skiplist::SkipList; 105 | /// 106 | /// let mut skiplist = SkipList::new(); 107 | /// skiplist.extend(0..10); 108 | /// skiplist.clear(); 109 | /// assert!(skiplist.is_empty()); 110 | /// ``` 111 | #[inline] 112 | pub fn clear(&mut self) { 113 | self.len = 0; 114 | *self.head = SkipNode::head(self.level_generator.total()); 115 | } 116 | 117 | /// Returns the number of elements in the skiplist. 118 | /// 119 | /// # Examples 120 | /// 121 | /// ``` 122 | /// use skiplist::SkipList; 123 | /// 124 | /// let mut skiplist = SkipList::new(); 125 | /// skiplist.extend(0..10); 126 | /// assert_eq!(skiplist.len(), 10); 127 | /// ``` 128 | #[inline] 129 | pub fn len(&self) -> usize { 130 | self.len 131 | } 132 | 133 | /// Returns `true` if the skiplist contains no elements. 134 | /// 135 | /// # Examples 136 | /// 137 | /// ``` 138 | /// use skiplist::SkipList; 139 | /// 140 | /// let mut skiplist = SkipList::new(); 141 | /// assert!(skiplist.is_empty()); 142 | /// 143 | /// skiplist.push_back(1); 144 | /// assert!(!skiplist.is_empty()); 145 | /// ``` 146 | #[inline] 147 | pub fn is_empty(&self) -> bool { 148 | self.len == 0 149 | } 150 | 151 | /// Insert the element into the skiplist at the given index, shifting all 152 | /// subsequent nodes down. 153 | /// 154 | /// # Panics 155 | /// 156 | /// Panics if the insert index is greater than the length of the skiplist. 157 | /// 158 | /// # Examples 159 | /// 160 | /// ``` 161 | /// use skiplist::SkipList; 162 | /// 163 | /// let mut skiplist = SkipList::new(); 164 | /// 165 | /// skiplist.insert(0, 0); 166 | /// skiplist.insert(5, 1); 167 | /// assert_eq!(skiplist.len(), 2); 168 | /// assert!(!skiplist.is_empty()); 169 | /// ``` 170 | pub fn insert(&mut self, value: T, index: usize) { 171 | if index > self.len() { 172 | panic!("Index out of bounds."); 173 | } 174 | self.len += 1; 175 | let new_node = Box::new(SkipNode::new(value, self.level_generator.level())); 176 | self.head 177 | .insert_at(new_node, index) 178 | .unwrap_or_else(|_| panic!("No insertion position is found!")); 179 | } 180 | 181 | /// Insert the element into the front of the skiplist. 182 | /// 183 | /// # Examples 184 | /// 185 | /// ``` 186 | /// use skiplist::SkipList; 187 | /// 188 | /// let mut skiplist = SkipList::new(); 189 | /// skiplist.push_front(1); 190 | /// skiplist.push_front(2); 191 | /// ``` 192 | pub fn push_front(&mut self, value: T) { 193 | self.insert(value, 0); 194 | } 195 | 196 | /// Insert the element into the back of the skiplist. 197 | /// 198 | /// # Examples 199 | /// 200 | /// ``` 201 | /// use skiplist::SkipList; 202 | /// 203 | /// let mut skiplist = SkipList::new(); 204 | /// skiplist.push_back(1); 205 | /// skiplist.push_back(2); 206 | /// ``` 207 | pub fn push_back(&mut self, value: T) { 208 | let len = self.len(); 209 | self.insert(value, len); 210 | } 211 | 212 | /// Provides a reference to the front element, or `None` if the skiplist is 213 | /// empty. 214 | /// 215 | /// # Examples 216 | /// 217 | /// ``` 218 | /// use skiplist::SkipList; 219 | /// 220 | /// let mut skiplist = SkipList::new(); 221 | /// assert!(skiplist.front().is_none()); 222 | /// 223 | /// skiplist.push_back(1); 224 | /// skiplist.push_back(2); 225 | /// assert_eq!(skiplist.front(), Some(&1)); 226 | /// ``` 227 | #[inline] 228 | pub fn front(&self) -> Option<&T> { 229 | if self.is_empty() { None } else { self.get(0) } 230 | } 231 | 232 | /// Provides a mutable reference to the front element, or `None` if the 233 | /// skiplist is empty. 234 | /// 235 | /// # Examples 236 | /// 237 | /// ``` 238 | /// use skiplist::SkipList; 239 | /// 240 | /// let mut skiplist = SkipList::new(); 241 | /// assert!(skiplist.front().is_none()); 242 | /// 243 | /// skiplist.push_back(1); 244 | /// skiplist.push_back(2); 245 | /// assert_eq!(skiplist.front_mut(), Some(&mut 1)); 246 | /// ``` 247 | #[inline] 248 | pub fn front_mut(&mut self) -> Option<&mut T> { 249 | if self.is_empty() { 250 | None 251 | } else { 252 | self.get_mut(0) 253 | } 254 | } 255 | 256 | /// Provides a reference to the back element, or `None` if the skiplist is 257 | /// empty. 258 | /// 259 | /// # Examples 260 | /// 261 | /// ``` 262 | /// use skiplist::SkipList; 263 | /// 264 | /// let mut skiplist = SkipList::new(); 265 | /// assert!(skiplist.back().is_none()); 266 | /// 267 | /// skiplist.push_back(1); 268 | /// skiplist.push_back(2); 269 | /// assert_eq!(skiplist.back(), Some(&2)); 270 | /// ``` 271 | #[inline] 272 | pub fn back(&self) -> Option<&T> { 273 | let len = self.len(); 274 | if len > 0 { self.get(len - 1) } else { None } 275 | } 276 | 277 | /// Provides a reference to the back element, or `None` if the skiplist is 278 | /// empty. 279 | /// 280 | /// # Examples 281 | /// 282 | /// ``` 283 | /// use skiplist::SkipList; 284 | /// 285 | /// let mut skiplist = SkipList::new(); 286 | /// assert!(skiplist.back().is_none()); 287 | /// 288 | /// skiplist.push_back(1); 289 | /// skiplist.push_back(2); 290 | /// assert_eq!(skiplist.back_mut(), Some(&mut 2)); 291 | /// ``` 292 | #[inline] 293 | pub fn back_mut(&mut self) -> Option<&mut T> { 294 | let len = self.len(); 295 | if len > 0 { self.get_mut(len - 1) } else { None } 296 | } 297 | 298 | /// Provides a reference to the element at the given index, or `None` if the 299 | /// skiplist is empty or the index is out of bounds. 300 | /// 301 | /// # Examples 302 | /// 303 | /// ``` 304 | /// use skiplist::SkipList; 305 | /// 306 | /// let mut skiplist = SkipList::new(); 307 | /// assert!(skiplist.get(0).is_none()); 308 | /// skiplist.extend(0..10); 309 | /// assert_eq!(skiplist.get(0), Some(&0)); 310 | /// assert!(skiplist.get(10).is_none()); 311 | /// ``` 312 | #[inline] 313 | pub fn get(&self, index: usize) -> Option<&T> { 314 | self.get_index(index).and_then(|node| node.item.as_ref()) 315 | } 316 | 317 | /// Provides a mutable reference to the element at the given index, or 318 | /// `None` if the skiplist is empty or the index is out of bounds. 319 | /// 320 | /// # Examples 321 | /// 322 | /// ``` 323 | /// use skiplist::SkipList; 324 | /// 325 | /// let mut skiplist = SkipList::new(); 326 | /// assert!(skiplist.get_mut(0).is_none()); 327 | /// skiplist.extend(0..10); 328 | /// assert_eq!(skiplist.get_mut(0), Some(&mut 0)); 329 | /// assert!(skiplist.get_mut(10).is_none()); 330 | /// ``` 331 | #[inline] 332 | pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { 333 | self.get_index_mut(index) 334 | .and_then(|node| node.item.as_mut()) 335 | } 336 | 337 | /// Removes the first element and returns it, or `None` if the sequence is 338 | /// empty. 339 | /// 340 | /// # Examples 341 | /// 342 | /// ``` 343 | /// use skiplist::SkipList; 344 | /// 345 | /// let mut skiplist = SkipList::new(); 346 | /// skiplist.push_back(1); 347 | /// skiplist.push_back(2); 348 | /// 349 | /// assert_eq!(skiplist.pop_front(), Some(1)); 350 | /// assert_eq!(skiplist.pop_front(), Some(2)); 351 | /// assert!(skiplist.pop_front().is_none()); 352 | /// ``` 353 | #[inline] 354 | pub fn pop_front(&mut self) -> Option { 355 | if self.is_empty() { 356 | None 357 | } else { 358 | Some(self.remove(0)) 359 | } 360 | } 361 | 362 | /// Removes the last element and returns it, or `None` if the sequence is 363 | /// empty. 364 | /// 365 | /// # Examples 366 | /// 367 | /// ``` 368 | /// use skiplist::SkipList; 369 | /// 370 | /// let mut skiplist = SkipList::new(); 371 | /// skiplist.push_back(1); 372 | /// skiplist.push_back(2); 373 | /// 374 | /// assert_eq!(skiplist.pop_back(), Some(2)); 375 | /// assert_eq!(skiplist.pop_back(), Some(1)); 376 | /// assert!(skiplist.pop_back().is_none()); 377 | /// ``` 378 | #[inline] 379 | pub fn pop_back(&mut self) -> Option { 380 | let len = self.len(); 381 | if len > 0 { 382 | Some(self.remove(len - 1)) 383 | } else { 384 | None 385 | } 386 | } 387 | 388 | /// Removes and returns an element with the given index. 389 | /// 390 | /// # Panics 391 | /// 392 | /// Panics is the index is out of bounds. 393 | /// 394 | /// # Examples 395 | /// 396 | /// ``` 397 | /// use skiplist::SkipList; 398 | /// 399 | /// let mut skiplist = SkipList::new(); 400 | /// skiplist.extend(0..10); 401 | /// assert_eq!(skiplist.remove(4), 4); 402 | /// assert_eq!(skiplist.remove(4), 5); 403 | /// ``` 404 | pub fn remove(&mut self, index: usize) -> T { 405 | if index >= self.len() { 406 | panic!("Index out of bounds."); 407 | } else { 408 | let node = self.head.remove_at(index).unwrap(); 409 | self.len -= 1; 410 | node.into_inner().unwrap() 411 | } 412 | } 413 | 414 | /// Retains only the elements specified by the predicate. 415 | /// 416 | /// In other words, remove all elements `e` such that `f(&e)` returns false. 417 | /// This method operates in place. 418 | /// 419 | /// # Examples 420 | /// 421 | /// ``` 422 | /// use skiplist::SkipList; 423 | /// 424 | /// let mut skiplist = SkipList::new(); 425 | /// skiplist.extend(0..10); 426 | /// skiplist.retain(|&x| x%2 == 0); 427 | /// ``` 428 | pub fn retain(&mut self, mut f: F) 429 | where 430 | F: FnMut(&T) -> bool, 431 | { 432 | self.len -= self.head.retain(move |_, x| f(x)); 433 | } 434 | 435 | /// Get an owning iterator over the entries of the skiplist. 436 | /// 437 | /// # Examples 438 | /// 439 | /// ``` 440 | /// use skiplist::SkipList; 441 | /// 442 | /// let mut skiplist = SkipList::new(); 443 | /// skiplist.extend(0..10); 444 | /// for i in skiplist.into_iter() { 445 | /// println!("Value: {}", i); 446 | /// } 447 | /// ``` 448 | #[allow(clippy::should_implement_trait)] 449 | pub fn into_iter(mut self) -> IntoIter { 450 | let len = self.len(); 451 | unsafe { IntoIter::from_head(&mut self.head, len) } 452 | } 453 | 454 | /// Creates an iterator over the entries of the skiplist. 455 | /// 456 | /// # Examples 457 | /// 458 | /// ``` 459 | /// use skiplist::SkipList; 460 | /// 461 | /// let mut skiplist = SkipList::new(); 462 | /// skiplist.extend(0..10); 463 | /// for i in skiplist.iter() { 464 | /// println!("Value: {}", i); 465 | /// } 466 | /// ``` 467 | pub fn iter(&self) -> Iter { 468 | unsafe { Iter::from_head(&self.head, self.len()) } 469 | } 470 | 471 | /// Creates an mutable iterator over the entries of the skiplist. 472 | /// 473 | /// # Examples 474 | /// 475 | /// ``` 476 | /// use skiplist::SkipList; 477 | /// 478 | /// let mut skiplist = SkipList::new(); 479 | /// skiplist.extend(0..10); 480 | /// for i in skiplist.iter_mut() { 481 | /// println!("Value: {}", i); 482 | /// } 483 | /// ``` 484 | pub fn iter_mut(&mut self) -> IterMut { 485 | let len = self.len(); 486 | unsafe { IterMut::from_head(&mut self.head, len) } 487 | } 488 | 489 | /// Constructs a double-ended iterator over a sub-range of elements in the 490 | /// skiplist, starting at min, and ending at max. If min is `Unbounded`, 491 | /// then it will be treated as "negative infinity", and if max is 492 | /// `Unbounded`, then it will be treated as "positive infinity". Thus 493 | /// range(Unbounded, Unbounded) will yield the whole collection. 494 | /// 495 | /// # Examples 496 | /// 497 | /// ``` 498 | /// use skiplist::SkipList; 499 | /// use std::ops::Bound::{Included, Unbounded}; 500 | /// 501 | /// let mut skiplist = SkipList::new(); 502 | /// skiplist.extend(0..10); 503 | /// for i in skiplist.range(Included(3), Included(7)) { 504 | /// println!("Value: {}", i); 505 | /// } 506 | /// assert_eq!(Some(&4), skiplist.range(Included(4), Unbounded).next()); 507 | /// ``` 508 | pub fn range(&self, min: Bound, max: Bound) -> Iter { 509 | let first = Self::_lower_bound(min); 510 | let last = self._upper_bound(max); 511 | self.iter_range(first, last) 512 | } 513 | 514 | /// Constructs a mutable double-ended iterator over a sub-range of elements 515 | /// in the skiplist, starting at min, and ending at max. If min is 516 | /// `Unbounded`, then it will be treated as "negative infinity", and if max 517 | /// is `Unbounded`, then it will be treated as "positive infinity". Thus 518 | /// range(Unbounded, Unbounded) will yield the whole collection. 519 | /// 520 | /// # Examples 521 | /// 522 | /// ``` 523 | /// use skiplist::SkipList; 524 | /// use std::ops::Bound::{Included, Unbounded}; 525 | /// 526 | /// let mut skiplist = SkipList::new(); 527 | /// skiplist.extend(0..10); 528 | /// for i in skiplist.range_mut(Included(3), Included(7)) { 529 | /// println!("Value: {}", i); 530 | /// } 531 | /// assert_eq!(Some(&mut 4), skiplist.range_mut(Included(4), Unbounded).next()); 532 | /// ``` 533 | pub fn range_mut(&mut self, min: Bound, max: Bound) -> IterMut { 534 | let first = match min { 535 | Bound::Included(i) => i, 536 | Bound::Excluded(i) => i + 1, 537 | Bound::Unbounded => 0, 538 | }; 539 | let last = match max { 540 | Bound::Included(i) => i, 541 | Bound::Excluded(i) => i - 1, 542 | Bound::Unbounded => self.len() - 1, 543 | }; 544 | self.iter_range_mut(first, last) 545 | } 546 | 547 | /// Returns an `Option<&T>` pointing to the lowest element whose key is above 548 | /// the given bound. If no such element is found then `None` is 549 | /// returned. 550 | /// 551 | /// # Examples 552 | /// 553 | /// ``` 554 | /// use skiplist::SkipList; 555 | /// use std::ops::Bound::{Included, Excluded, Unbounded}; 556 | /// 557 | /// let mut skiplist = SkipList::new(); 558 | /// skiplist.extend(0..10); 559 | /// 560 | /// assert_eq!(skiplist.lower_bound(Unbounded), Some(&0)); 561 | /// assert_eq!(skiplist.lower_bound(Excluded(0)), Some(&1)); 562 | /// assert_eq!(skiplist.lower_bound(Included(0)), Some(&0)); 563 | /// assert_eq!(skiplist.lower_bound(Excluded(10)), None); 564 | /// ``` 565 | pub fn lower_bound(&self, min: Bound) -> Option<&T> { 566 | self.get_index(Self::_lower_bound(min)) 567 | .and_then(|node| node.item.as_ref()) 568 | } 569 | 570 | /// Returns an `Option<&T>` pointing to the highest element whose key is above 571 | /// the given bound. If no such element is found then `None` is 572 | /// returned. 573 | /// 574 | /// # Examples 575 | /// 576 | /// ``` 577 | /// use skiplist::SkipList; 578 | /// use std::ops::Bound::{Included, Excluded, Unbounded}; 579 | /// 580 | /// let mut skiplist = SkipList::new(); 581 | /// skiplist.extend(0..10); 582 | /// 583 | /// assert_eq!(skiplist.upper_bound(Unbounded), Some(&9)); 584 | /// assert_eq!(skiplist.upper_bound(Excluded(9)), Some(&8)); 585 | /// assert_eq!(skiplist.upper_bound(Included(9)), Some(&9)); 586 | /// ``` 587 | pub fn upper_bound(&self, max: Bound) -> Option<&T> { 588 | self.get_index(self._upper_bound(max)) 589 | .and_then(|node| node.item.as_ref()) 590 | } 591 | 592 | fn _lower_bound(min: Bound) -> usize { 593 | match min { 594 | Bound::Included(i) => i, 595 | Bound::Excluded(i) => i + 1, 596 | Bound::Unbounded => 0, 597 | } 598 | } 599 | 600 | fn _upper_bound(&self, max: Bound) -> usize { 601 | match max { 602 | Bound::Included(i) => i, 603 | Bound::Excluded(i) => i - 1, 604 | Bound::Unbounded => self.len() - 1, 605 | } 606 | } 607 | } 608 | 609 | impl SkipList 610 | where 611 | T: PartialEq, 612 | G: LevelGenerator, 613 | { 614 | /// Returns true if the value is contained in the skiplist. 615 | /// 616 | /// # Examples 617 | /// 618 | /// ``` 619 | /// use skiplist::SkipList; 620 | /// 621 | /// let mut skiplist = SkipList::new(); 622 | /// skiplist.extend(0..10); 623 | /// assert!(skiplist.contains(&4)); 624 | /// assert!(!skiplist.contains(&15)); 625 | /// ``` 626 | pub fn contains(&self, value: &T) -> bool { 627 | self.iter().any(|val| val.eq(value)) 628 | } 629 | 630 | /// Returns the index of the value if it's contained in the skiplist or None otherwise. 631 | /// 632 | /// Since this skip list is not ordered, this method has a complexity of O(n) 633 | /// 634 | /// # Examples 635 | /// 636 | /// ``` 637 | /// use skiplist::SkipList; 638 | /// 639 | /// let mut skiplist = SkipList::new(); 640 | /// skiplist.extend(0..10); 641 | /// assert_eq!(skiplist.index_of(&4), 4); 642 | /// ``` 643 | #[inline] 644 | pub fn index_of(&self, item: &T) -> Option { 645 | self.iter() 646 | .enumerate() 647 | .find_map(|(idx, it)| if item.eq(it) { Some(idx) } else { None }) 648 | } 649 | 650 | /// Removes all consecutive repeated elements in the skiplist. 651 | /// 652 | /// # Examples 653 | /// 654 | /// ``` 655 | /// use skiplist::SkipList; 656 | /// 657 | /// let mut skiplist = SkipList::new(); 658 | /// skiplist.push_back(0); 659 | /// skiplist.push_back(0); 660 | /// assert_eq!(skiplist.len(), 2); 661 | /// skiplist.dedup(); 662 | /// assert_eq!(skiplist.len(), 1); 663 | /// ``` 664 | pub fn dedup(&mut self) { 665 | let removed = self 666 | .head 667 | .retain(|prev, current| prev.is_none_or(|prev| !prev.eq(current))); 668 | self.len -= removed; 669 | } 670 | } 671 | 672 | // /////////////////////////////////////////////// 673 | // Internal methods 674 | // /////////////////////////////////////////////// 675 | 676 | impl SkipList 677 | where 678 | G: LevelGenerator, 679 | { 680 | /// Checks the integrity of the skiplist. 681 | #[allow(dead_code)] 682 | fn check(&self) { 683 | self.head.check(); 684 | } 685 | 686 | /// Makes an iterator between [begin, end] 687 | fn iter_range(&self, first_idx: usize, last_idx: usize) -> Iter { 688 | if first_idx > last_idx { 689 | return Iter { 690 | first: None, 691 | last: None, 692 | size: 0, 693 | }; 694 | } 695 | let first = self.get_index(first_idx); 696 | let last = self.get_index(last_idx); 697 | if first.is_some() && last.is_some() { 698 | Iter { 699 | first, 700 | last, 701 | size: last_idx - first_idx + 1, 702 | } 703 | } else { 704 | Iter { 705 | first: None, 706 | last: None, 707 | size: 0, 708 | } 709 | } 710 | } 711 | 712 | /// Makes an iterator between [begin, end] 713 | fn iter_range_mut(&mut self, first_idx: usize, last_idx: usize) -> IterMut { 714 | if first_idx > last_idx { 715 | return IterMut { 716 | first: None, 717 | last: None, 718 | size: 0, 719 | }; 720 | } 721 | let last = self.get_index_mut(last_idx).and_then(|p| NonNull::new(p)); 722 | let first = self.get_index_mut(first_idx); 723 | if first.is_some() && last.is_some() { 724 | IterMut { 725 | first, 726 | last, 727 | size: last_idx - first_idx + 1, 728 | } 729 | } else { 730 | IterMut { 731 | first: None, 732 | last: None, 733 | size: 0, 734 | } 735 | } 736 | } 737 | 738 | /// Gets a pointer to the node with the given index. 739 | fn get_index(&self, index: usize) -> Option<&SkipNode> { 740 | if self.len() <= index { 741 | None 742 | } else { 743 | self.head.advance(index + 1) 744 | } 745 | } 746 | 747 | fn get_index_mut(&mut self, index: usize) -> Option<&mut SkipNode> { 748 | if self.len() <= index { 749 | None 750 | } else { 751 | self.head.advance_mut(index + 1) 752 | } 753 | } 754 | } 755 | 756 | impl SkipList 757 | where 758 | T: fmt::Debug, 759 | { 760 | /// Prints out the internal structure of the skiplist (for debugging 761 | /// purposes). 762 | #[allow(dead_code)] 763 | fn debug_structure(&self) { 764 | unsafe { 765 | let mut node = self.head.as_ref(); 766 | let mut rows: Vec<_> = iter::repeat(String::new()) 767 | .take(self.level_generator.total()) 768 | .collect(); 769 | 770 | loop { 771 | let value = match node.item { 772 | Some(ref v) => { 773 | format!("> [{:?}]", v) 774 | } 775 | _ => "> []".to_string(), 776 | }; 777 | 778 | let max_str_len = format!("{} -{}-", value, node.links_len[node.level]).len() + 1; 779 | 780 | let mut lvl = self.level_generator.total(); 781 | while lvl > 0 { 782 | lvl -= 1; 783 | 784 | let mut value_len = if lvl <= node.level { 785 | format!("{} -{}-", value, node.links_len[lvl]) 786 | } else { 787 | format!("{} -", value) 788 | }; 789 | for _ in 0..(max_str_len - value_len.len()) { 790 | value_len.push('-'); 791 | } 792 | 793 | let mut dashes = String::new(); 794 | for _ in 0..value_len.len() { 795 | dashes.push('-'); 796 | } 797 | 798 | if lvl <= node.level { 799 | rows[lvl].push_str(value_len.as_ref()); 800 | } else { 801 | rows[lvl].push_str(dashes.as_ref()); 802 | } 803 | } 804 | 805 | if let Some(next) = node.links[0].and_then(|p| p.as_ptr().as_ref()) { 806 | node = next; 807 | } else { 808 | break; 809 | } 810 | } 811 | 812 | for row in rows.iter().rev() { 813 | println!("{}", row); 814 | } 815 | } 816 | } 817 | } 818 | 819 | // /////////////////////////////////////////////// 820 | // Trait implementation 821 | // /////////////////////////////////////////////// 822 | 823 | unsafe impl Send for SkipList {} 824 | unsafe impl Sync for SkipList {} 825 | 826 | impl default::Default for SkipList { 827 | fn default() -> SkipList { 828 | SkipList::new() 829 | } 830 | } 831 | 832 | /// This implementation of PartialEq only checks that the *values* are equal; it 833 | /// does not check for equivalence of other features (such as the ordering 834 | /// function and the node levels). Furthermore, this uses `T`'s implementation 835 | /// of PartialEq and *does not* use the owning skiplist's comparison function. 836 | impl cmp::PartialEq> for SkipList 837 | where 838 | A: cmp::PartialEq, 839 | { 840 | #[inline] 841 | fn eq(&self, other: &SkipList) -> bool { 842 | self.len() == other.len() && self.iter().eq(other) 843 | } 844 | #[allow(clippy::partialeq_ne_impl)] 845 | #[inline] 846 | fn ne(&self, other: &SkipList) -> bool { 847 | self.len != other.len || self.iter().ne(other) 848 | } 849 | } 850 | 851 | impl cmp::Eq for SkipList where T: cmp::Eq {} 852 | 853 | impl cmp::PartialOrd> for SkipList 854 | where 855 | A: cmp::PartialOrd, 856 | { 857 | #[inline] 858 | fn partial_cmp(&self, other: &SkipList) -> Option { 859 | self.iter().partial_cmp(other) 860 | } 861 | } 862 | 863 | impl Ord for SkipList 864 | where 865 | T: cmp::Ord, 866 | { 867 | #[inline] 868 | fn cmp(&self, other: &SkipList) -> Ordering { 869 | self.iter().cmp(other) 870 | } 871 | } 872 | 873 | impl Extend for SkipList { 874 | #[inline] 875 | fn extend>(&mut self, iterable: I) { 876 | let iterator = iterable.into_iter(); 877 | for element in iterator { 878 | self.push_back(element); 879 | } 880 | } 881 | } 882 | 883 | impl ops::Index for SkipList { 884 | type Output = T; 885 | 886 | fn index(&self, index: usize) -> &T { 887 | self.get(index).expect("Index out of range") 888 | } 889 | } 890 | 891 | impl ops::IndexMut for SkipList { 892 | fn index_mut(&mut self, index: usize) -> &mut Self::Output { 893 | self.get_mut(index).expect("Index out of range") 894 | } 895 | } 896 | 897 | impl fmt::Debug for SkipList 898 | where 899 | T: fmt::Debug, 900 | { 901 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 902 | write!(f, "[")?; 903 | 904 | for (i, entry) in self.iter().enumerate() { 905 | if i != 0 { 906 | write!(f, ", ")?; 907 | } 908 | write!(f, "{:?}", entry)?; 909 | } 910 | write!(f, "]") 911 | } 912 | } 913 | 914 | impl fmt::Display for SkipList 915 | where 916 | T: fmt::Display, 917 | { 918 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 919 | write!(f, "[")?; 920 | 921 | for (i, entry) in self.iter().enumerate() { 922 | if i != 0 { 923 | write!(f, ", ")?; 924 | } 925 | write!(f, "{}", entry)?; 926 | } 927 | write!(f, "]") 928 | } 929 | } 930 | 931 | impl iter::IntoIterator for SkipList { 932 | type Item = T; 933 | type IntoIter = IntoIter; 934 | 935 | fn into_iter(self) -> IntoIter { 936 | self.into_iter() 937 | } 938 | } 939 | impl<'a, T> iter::IntoIterator for &'a SkipList { 940 | type Item = &'a T; 941 | type IntoIter = Iter<'a, T>; 942 | 943 | fn into_iter(self) -> Iter<'a, T> { 944 | self.iter() 945 | } 946 | } 947 | impl<'a, T> iter::IntoIterator for &'a mut SkipList { 948 | type Item = &'a mut T; 949 | type IntoIter = IterMut<'a, T>; 950 | 951 | fn into_iter(self) -> IterMut<'a, T> { 952 | self.iter_mut() 953 | } 954 | } 955 | 956 | impl iter::FromIterator for SkipList { 957 | #[inline] 958 | fn from_iter(iter: I) -> SkipList 959 | where 960 | I: iter::IntoIterator, 961 | { 962 | let mut skiplist = SkipList::new(); 963 | skiplist.extend(iter); 964 | skiplist 965 | } 966 | } 967 | 968 | impl Hash for SkipList { 969 | #[inline] 970 | fn hash(&self, state: &mut H) { 971 | for elt in self { 972 | elt.hash(state); 973 | } 974 | } 975 | } 976 | 977 | // //////////////////////////////////////////////////////////////////////////// 978 | // Tests 979 | // //////////////////////////////////////////////////////////////////////////// 980 | 981 | #[cfg(test)] 982 | mod tests { 983 | use std::ops::Bound::{self, Excluded, Included, Unbounded}; 984 | 985 | use anyhow::Result; 986 | use rand::{ 987 | Rng, 988 | distr::{Uniform, uniform::UniformUsize}, 989 | }; 990 | 991 | use super::SkipList; 992 | 993 | #[test] 994 | fn push_front() { 995 | let mut sl = SkipList::new(); 996 | for i in (1..100).rev() { 997 | sl.push_front(i); 998 | } 999 | 1000 | assert!(sl.into_iter().eq(1..100)); 1001 | } 1002 | 1003 | #[test] 1004 | fn push_back() { 1005 | let mut sl = SkipList::new(); 1006 | for i in 1..100 { 1007 | sl.push_back(i); 1008 | } 1009 | assert!(sl.into_iter().eq(1..100)); 1010 | } 1011 | 1012 | #[test] 1013 | fn insert_rand() -> Result<()> { 1014 | let mut rng = rand::thread_rng(); 1015 | let mut sl: SkipList = SkipList::new(); 1016 | let mut vec: Vec = Vec::new(); 1017 | for i in 0..100 { 1018 | let idx = rng.sample(Uniform::new_inclusive(0, i)?); 1019 | sl.insert(i, idx); 1020 | vec.insert(idx, i); 1021 | } 1022 | assert_eq!(sl.into_iter().collect::>(), vec); 1023 | Ok(()) 1024 | } 1025 | 1026 | #[test] 1027 | fn insert_repeat() { 1028 | let mut sl = SkipList::new(); 1029 | let repeat = 10; 1030 | for val in 0..10 { 1031 | for i in 0..repeat { 1032 | sl.insert(val * 10 + i, val * 10); 1033 | sl.check(); 1034 | } 1035 | } 1036 | } 1037 | 1038 | #[test] 1039 | fn remove_rand() -> Result<()> { 1040 | let mut rng = rand::thread_rng(); 1041 | let mut v: Vec = (0..1000).collect(); 1042 | let mut sl: SkipList = (0..1000).collect(); 1043 | for i in (0..1000).rev() { 1044 | let idx = rng.sample(Uniform::new_inclusive(0, i)?); 1045 | assert_eq!(sl.remove(idx), v.remove(idx)); 1046 | } 1047 | Ok(()) 1048 | } 1049 | 1050 | #[test] 1051 | fn append_test() {} 1052 | 1053 | #[test] 1054 | fn basic_small() { 1055 | let mut sl: SkipList = SkipList::new(); 1056 | sl.check(); 1057 | sl.insert(1, 0); 1058 | sl.check(); 1059 | assert_eq!(sl.remove(0), 1); 1060 | sl.check(); 1061 | sl.insert(1, 0); 1062 | sl.check(); 1063 | sl.insert(2, 1); 1064 | sl.check(); 1065 | assert_eq!(sl.remove(0), 1); 1066 | sl.check(); 1067 | assert_eq!(sl.remove(0), 2); 1068 | sl.check(); 1069 | } 1070 | 1071 | #[test] 1072 | fn basic_large() { 1073 | let size = 500; 1074 | let mut sl = SkipList::with_capacity(500); 1075 | assert!(sl.is_empty()); 1076 | 1077 | for i in 0..size { 1078 | sl.insert(i, i); 1079 | assert_eq!(sl.len(), i + 1); 1080 | } 1081 | sl.check(); 1082 | 1083 | for i in 0..size { 1084 | assert_eq!(sl.remove(0), i); 1085 | assert_eq!(sl.len(), size - i - 1); 1086 | } 1087 | sl.check(); 1088 | 1089 | for i in 0..size { 1090 | sl = (0..size).collect(); 1091 | assert_eq!(sl.remove(i), i); 1092 | } 1093 | } 1094 | 1095 | #[test] 1096 | fn clear() { 1097 | let mut sl: SkipList = (0..100).collect(); 1098 | assert_eq!(sl.len(), 100); 1099 | sl.clear(); 1100 | sl.check(); 1101 | assert!(sl.is_empty()); 1102 | } 1103 | 1104 | #[test] 1105 | fn last() { 1106 | let mut sl = SkipList::new(); 1107 | assert_eq!(sl.iter().next_back(), None); 1108 | for i in 0..100 { 1109 | sl.push_back(i); 1110 | assert_eq!(sl.iter().next_back(), Some(&i)); 1111 | } 1112 | } 1113 | 1114 | #[test] 1115 | fn iter() { 1116 | let size = 10000; 1117 | 1118 | let mut sl: SkipList<_> = (0..size).collect(); 1119 | 1120 | fn test(size: usize, mut iter: T) 1121 | where 1122 | T: Iterator, 1123 | { 1124 | for i in 0..size { 1125 | assert_eq!(iter.size_hint(), (size - i, Some(size - i))); 1126 | assert_eq!(iter.next().unwrap(), i); 1127 | } 1128 | assert_eq!(iter.size_hint(), (0, Some(0))); 1129 | assert!(iter.next().is_none()); 1130 | } 1131 | test(size, sl.iter().copied()); 1132 | #[allow(clippy::map_clone)] 1133 | test(size, sl.iter_mut().map(|&mut i| i)); 1134 | test(size, sl.into_iter()); 1135 | } 1136 | 1137 | #[test] 1138 | fn iter_rev() { 1139 | let size = 10000; 1140 | 1141 | let mut sl: SkipList<_> = (0..size).collect(); 1142 | 1143 | fn test(size: usize, mut iter: T) 1144 | where 1145 | T: Iterator, 1146 | { 1147 | for i in 0..size { 1148 | assert_eq!(iter.size_hint(), (size - i, Some(size - i))); 1149 | assert_eq!(iter.next().unwrap(), size - i - 1); 1150 | } 1151 | assert_eq!(iter.size_hint(), (0, Some(0))); 1152 | assert!(iter.next().is_none()); 1153 | } 1154 | test(size, sl.iter().rev().copied()); 1155 | #[allow(clippy::map_clone)] 1156 | test(size, sl.iter_mut().rev().map(|&mut i| i)); 1157 | test(size, sl.into_iter().rev()); 1158 | } 1159 | 1160 | #[test] 1161 | fn iter_mixed() { 1162 | let size = 10000; 1163 | 1164 | let mut sl: SkipList<_> = (0..size).collect(); 1165 | 1166 | fn test(size: usize, mut iter: T) 1167 | where 1168 | T: Iterator + DoubleEndedIterator, 1169 | { 1170 | for i in 0..size / 4 { 1171 | assert_eq!(iter.size_hint(), (size - i * 2, Some(size - i * 2))); 1172 | assert_eq!(iter.next().unwrap(), i); 1173 | assert_eq!(iter.next_back().unwrap(), size - i - 1); 1174 | } 1175 | for i in size / 4..size * 3 / 4 { 1176 | assert_eq!(iter.size_hint(), (size * 3 / 4 - i, Some(size * 3 / 4 - i))); 1177 | assert_eq!(iter.next().unwrap(), i); 1178 | } 1179 | assert_eq!(iter.size_hint(), (0, Some(0))); 1180 | assert!(iter.next().is_none()); 1181 | } 1182 | test(size, sl.iter().copied()); 1183 | #[allow(clippy::map_clone)] 1184 | test(size, sl.iter_mut().map(|&mut i| i)); 1185 | test(size, sl.into_iter()); 1186 | } 1187 | 1188 | #[test] 1189 | fn range_small() { 1190 | let size = 5; 1191 | 1192 | let sl: SkipList<_> = (0..size).collect(); 1193 | 1194 | let mut j = 0; 1195 | for (&v, i) in sl.range(Included(2), Unbounded).zip(2..size) { 1196 | assert_eq!(v, i); 1197 | j += 1; 1198 | } 1199 | assert_eq!(j, size - 2); 1200 | } 1201 | 1202 | #[test] 1203 | fn range_1000() { 1204 | let size = 1000; 1205 | let sl: SkipList<_> = (0..size).collect(); 1206 | 1207 | fn test(sl: &SkipList, min: Bound, max: Bound) { 1208 | let mut values = sl.range(min, max); 1209 | #[allow(clippy::range_plus_one)] 1210 | let mut expects = match (min, max) { 1211 | (Excluded(a), Excluded(b)) => (a + 1)..b, 1212 | (Included(a), Excluded(b)) => a..b, 1213 | (Unbounded, Excluded(b)) => 0..b, 1214 | (Excluded(a), Included(b)) => (a + 1)..(b + 1), 1215 | (Included(a), Included(b)) => a..(b + 1), 1216 | (Unbounded, Included(b)) => 0..(b + 1), 1217 | (Excluded(a), Unbounded) => (a + 1)..1000, 1218 | (Included(a), Unbounded) => a..1000, 1219 | (Unbounded, Unbounded) => 0..1000, 1220 | }; 1221 | 1222 | assert_eq!(values.size_hint(), expects.size_hint()); 1223 | 1224 | for (&v, e) in values.by_ref().zip(expects.by_ref()) { 1225 | assert_eq!(v, e); 1226 | } 1227 | assert!(values.next().is_none()); 1228 | assert!(expects.next().is_none()); 1229 | } 1230 | 1231 | test(&sl, Excluded(200), Excluded(800)); 1232 | test(&sl, Included(200), Excluded(800)); 1233 | test(&sl, Unbounded, Excluded(800)); 1234 | test(&sl, Excluded(200), Included(800)); 1235 | test(&sl, Included(200), Included(800)); 1236 | test(&sl, Unbounded, Included(800)); 1237 | test(&sl, Excluded(200), Unbounded); 1238 | test(&sl, Included(200), Unbounded); 1239 | test(&sl, Unbounded, Unbounded); 1240 | } 1241 | 1242 | #[test] 1243 | fn range_mut_1000() { 1244 | let size = 1000; 1245 | let mut sl: SkipList<_> = (0..size).collect(); 1246 | 1247 | fn test(sl: &mut SkipList, min: Bound, max: Bound) { 1248 | let mut values = sl.range(min, max); 1249 | #[allow(clippy::range_plus_one)] 1250 | let mut expects = match (min, max) { 1251 | (Excluded(a), Excluded(b)) => (a + 1)..b, 1252 | (Included(a), Excluded(b)) => a..b, 1253 | (Unbounded, Excluded(b)) => 0..b, 1254 | (Excluded(a), Included(b)) => (a + 1)..(b + 1), 1255 | (Included(a), Included(b)) => a..(b + 1), 1256 | (Unbounded, Included(b)) => 0..(b + 1), 1257 | (Excluded(a), Unbounded) => (a + 1)..1000, 1258 | (Included(a), Unbounded) => a..1000, 1259 | (Unbounded, Unbounded) => 0..1000, 1260 | }; 1261 | assert_eq!(values.size_hint(), expects.size_hint()); 1262 | 1263 | for (&v, e) in values.by_ref().zip(expects.by_ref()) { 1264 | assert_eq!(v, e); 1265 | } 1266 | assert!(values.next().is_none()); 1267 | assert!(expects.next().is_none()); 1268 | } 1269 | 1270 | test(&mut sl, Excluded(200), Excluded(800)); 1271 | test(&mut sl, Included(200), Excluded(800)); 1272 | test(&mut sl, Unbounded, Excluded(800)); 1273 | test(&mut sl, Excluded(200), Included(800)); 1274 | test(&mut sl, Included(200), Included(800)); 1275 | test(&mut sl, Unbounded, Included(800)); 1276 | test(&mut sl, Excluded(200), Unbounded); 1277 | test(&mut sl, Included(200), Unbounded); 1278 | test(&mut sl, Unbounded, Unbounded); 1279 | } 1280 | 1281 | #[test] 1282 | fn range() { 1283 | let size = 200; 1284 | let sl: SkipList<_> = (0..size).collect(); 1285 | 1286 | for i in 0..size { 1287 | for j in 0..size { 1288 | let mut values = sl.range(Included(i), Included(j)); 1289 | let mut expects = i..=j; 1290 | 1291 | for (&v, e) in values.by_ref().zip(expects.by_ref()) { 1292 | assert_eq!(v, e); 1293 | } 1294 | assert!(values.next().is_none()); 1295 | assert!(expects.next().is_none()); 1296 | } 1297 | } 1298 | 1299 | for i in 0..size { 1300 | for j in 0..size { 1301 | let mut values = sl.range(Included(i), Included(j)).rev(); 1302 | let mut expects = (i..=j).rev(); 1303 | 1304 | assert_eq!(values.size_hint(), expects.size_hint()); 1305 | 1306 | for (&v, e) in values.by_ref().zip(expects.by_ref()) { 1307 | assert_eq!(v, e); 1308 | } 1309 | assert!(values.next().is_none()); 1310 | assert!(expects.next().is_none()); 1311 | } 1312 | } 1313 | } 1314 | 1315 | #[test] 1316 | fn index_of() { 1317 | let size = 200; 1318 | let sl: SkipList<_> = (0..size).collect(); 1319 | for val in 0..size { 1320 | let i = sl.index_of(&val).expect("Index can't be None"); 1321 | let item_at_index = sl.get(i).expect("Item can't be None"); 1322 | assert_eq!(&val, item_at_index); 1323 | } 1324 | } 1325 | 1326 | #[test] 1327 | fn bound() { 1328 | let sl: SkipList<_> = (0..2).collect(); 1329 | 1330 | assert_eq!(sl.lower_bound(Unbounded), Some(&0)); 1331 | assert_eq!(sl.lower_bound(Excluded(0)), Some(&1)); 1332 | assert_eq!(sl.lower_bound(Included(0)), Some(&0)); 1333 | 1334 | assert_eq!(sl.lower_bound(Excluded(2)), None); 1335 | assert_eq!(sl.lower_bound(Included(2)), None); 1336 | 1337 | assert_eq!(sl.upper_bound(Unbounded), Some(&1)); 1338 | assert_eq!(sl.upper_bound(Excluded(1)), Some(&0)); 1339 | assert_eq!(sl.upper_bound(Included(1)), Some(&1)); 1340 | } 1341 | 1342 | #[test] 1343 | fn index_pop() { 1344 | let size = 1000; 1345 | let mut sl: SkipList<_> = (0..size).collect(); 1346 | assert_eq!(sl.front(), Some(&0)); 1347 | assert_eq!(sl.front_mut(), Some(&mut 0)); 1348 | assert_eq!(sl.back(), Some(&(size - 1))); 1349 | assert_eq!(sl.back_mut(), Some(&mut (size - 1))); 1350 | for mut i in 0..size { 1351 | assert_eq!(sl[i], i); 1352 | assert_eq!(sl.get(i), Some(&i)); 1353 | assert_eq!(sl.get_mut(i), Some(&mut i)) 1354 | } 1355 | 1356 | let mut sl: SkipList<_> = (0..size).collect(); 1357 | for i in 0..size { 1358 | assert_eq!(sl.pop_front(), Some(i)); 1359 | assert_eq!(sl.len(), size - i - 1); 1360 | } 1361 | assert!(sl.pop_front().is_none()); 1362 | assert!(sl.front().is_none()); 1363 | assert!(sl.is_empty()); 1364 | 1365 | let mut sl: SkipList<_> = (0..size).collect(); 1366 | for i in 0..size { 1367 | assert_eq!(sl.pop_back(), Some(size - i - 1)); 1368 | assert_eq!(sl.len(), size - i - 1); 1369 | } 1370 | assert!(sl.pop_back().is_none()); 1371 | assert!(sl.back().is_none()); 1372 | assert!(sl.is_empty()); 1373 | } 1374 | 1375 | #[test] 1376 | fn contains() { 1377 | let (min, max) = (25, 75); 1378 | let sl: SkipList<_> = (min..max).collect(); 1379 | 1380 | for i in 0..100 { 1381 | if i < min || i >= max { 1382 | assert!(!sl.contains(&i)); 1383 | } else { 1384 | assert!(sl.contains(&i)); 1385 | } 1386 | } 1387 | } 1388 | 1389 | #[test] 1390 | fn inplace_mut() { 1391 | let size = 1000; 1392 | let mut sl: SkipList<_> = (0..size).collect(); 1393 | 1394 | for i in 0..size { 1395 | let v = sl.get_mut(i).unwrap(); 1396 | *v *= 2; 1397 | } 1398 | 1399 | for i in 0..size { 1400 | assert_eq!(sl.get(i), Some(&(2 * i))); 1401 | } 1402 | } 1403 | 1404 | #[test] 1405 | fn dedup() { 1406 | let size = 1000; 1407 | let repeats = 10; 1408 | 1409 | let mut sl: SkipList = SkipList::new(); 1410 | for i in 0..size { 1411 | for _ in 0..repeats { 1412 | sl.insert(i, i * repeats); 1413 | } 1414 | } 1415 | { 1416 | let mut iter = sl.iter(); 1417 | for i in 0..size { 1418 | for _ in 0..repeats { 1419 | assert_eq!(iter.next(), Some(&i)); 1420 | } 1421 | } 1422 | } 1423 | sl.dedup(); 1424 | sl.check(); 1425 | let mut iter = sl.iter(); 1426 | for i in 0..size { 1427 | assert_eq!(iter.next(), Some(&i)); 1428 | } 1429 | } 1430 | 1431 | #[test] 1432 | fn retain() { 1433 | let repeats = 10; 1434 | let size = 100; 1435 | 1436 | let mut sl: SkipList = SkipList::new(); 1437 | for i in 0..size { 1438 | for _ in 0..repeats { 1439 | sl.insert(i, i * repeats); 1440 | sl.check(); 1441 | } 1442 | } 1443 | { 1444 | let mut iter = sl.iter(); 1445 | for i in 0..size { 1446 | for _ in 0..repeats { 1447 | assert_eq!(iter.next(), Some(&i)); 1448 | } 1449 | } 1450 | } 1451 | sl.retain(|&x| x % 5 == 0); 1452 | sl.debug_structure(); 1453 | sl.check(); 1454 | assert_eq!(sl.len(), repeats * size / 5); 1455 | 1456 | { 1457 | let mut iter = sl.iter(); 1458 | for i in 0..size / 5 { 1459 | for _ in 0..repeats { 1460 | assert_eq!(iter.next(), Some(&(i * 5))); 1461 | } 1462 | } 1463 | } 1464 | sl.retain(|&_| false); 1465 | sl.check(); 1466 | assert!(sl.is_empty()); 1467 | } 1468 | 1469 | #[test] 1470 | fn debug_display() { 1471 | let sl: SkipList<_> = (0..10).collect(); 1472 | sl.debug_structure(); 1473 | println!("{:?}", sl); 1474 | println!("{}", sl); 1475 | } 1476 | 1477 | #[test] 1478 | #[allow(clippy::eq_op, clippy::many_single_char_names)] 1479 | fn equality() { 1480 | let a: SkipList = (0..100).collect(); 1481 | let b: SkipList = (0..100).collect(); 1482 | let c: SkipList = (0..10).collect(); 1483 | let d: SkipList = (100..200).collect(); 1484 | let e: SkipList = (0..100).chain(0..1).collect(); 1485 | 1486 | assert_eq!(a, a); 1487 | assert_eq!(a, b); 1488 | assert_ne!(a, c); 1489 | assert_ne!(a, d); 1490 | assert_ne!(a, e); 1491 | assert_eq!(b, b); 1492 | assert_ne!(b, c); 1493 | assert_ne!(b, d); 1494 | assert_ne!(b, e); 1495 | assert_eq!(c, c); 1496 | assert_ne!(c, d); 1497 | assert_ne!(c, e); 1498 | assert_eq!(d, d); 1499 | assert_ne!(d, e); 1500 | assert_eq!(e, e); 1501 | } 1502 | } 1503 | -------------------------------------------------------------------------------- /src/skipnode.rs: -------------------------------------------------------------------------------- 1 | #![allow(warnings)] 2 | #![allow(clippy)] 3 | #![allow(unknown_lints)] 4 | 5 | use std::{ 6 | cmp::Ordering, 7 | fmt, iter, 8 | ptr::{self, NonNull}, 9 | }; 10 | 11 | // //////////////////////////////////////////////////////////////////////////// 12 | // SkipNode 13 | // //////////////////////////////////////////////////////////////////////////// 14 | 15 | /// A covariant pointer to a SkipNode. 16 | /// 17 | /// SkipNode should contain mutable pointers to other nodes, 18 | /// but mutable pointers are not covariant in Rust. 19 | /// The appropriate pointer type is std::ptr::NonNull. 20 | /// 21 | /// See [`std::ptr::NonNull`] and Rustonomicon for details on covariance. 22 | /// https://doc.rust-lang.org/nomicon/subtyping.html 23 | type Link = Option>>; 24 | 25 | /// SkipNodes are make up the SkipList. The SkipList owns the first head-node 26 | /// (which has no value) and each node has ownership of the next node through 27 | /// `next`. 28 | /// 29 | /// The node has a `level` which corresponds to how 'high' the node reaches. 30 | /// 31 | /// A node of `level` n has (n + 1) links to next nodes, which are stored in 32 | /// a vector. 33 | /// 34 | /// The node linked by level 0 should be considered owned by this node. 35 | /// 36 | /// There is a corresponding vector of link lengths which contains the distance 37 | /// between current node and the next node. If there's no next node, the distance 38 | /// is distance between current node and last reachable node. 39 | /// 40 | /// Lastly, each node contains a link to the immediately previous node in case 41 | /// one needs to parse the list backwards. 42 | #[derive(Clone, Debug)] 43 | pub struct SkipNode { 44 | // item should never be None, unless the node is a head. 45 | pub item: Option, 46 | // how high the node reaches. 47 | pub level: usize, 48 | // The immediately previous element. 49 | pub prev: Link, 50 | // Vector of links to the next node at the respective level. This vector 51 | // *must* be of length `self.level + 1`. links[0] stores a pointer to the 52 | // next node, which will have to be dropped. 53 | pub links: Vec>, 54 | // The corresponding length of each link 55 | pub links_len: Vec, 56 | } 57 | 58 | // /////////////////////////////////////////////// 59 | // Inherent methods 60 | // /////////////////////////////////////////////// 61 | 62 | impl SkipNode { 63 | /// Create a new head node. 64 | pub fn head(total_levels: usize) -> Self { 65 | SkipNode { 66 | item: None, 67 | level: total_levels - 1, 68 | prev: None, 69 | links: iter::repeat(None).take(total_levels).collect(), 70 | links_len: iter::repeat(0).take(total_levels).collect(), 71 | } 72 | } 73 | 74 | /// Create a new SkipNode with the given item.. 75 | /// All pointers default to null. 76 | pub fn new(item: V, level: usize) -> Self { 77 | SkipNode { 78 | item: Some(item), 79 | level, 80 | prev: None, 81 | links: iter::repeat(None).take(level + 1).collect(), 82 | links_len: iter::repeat(0).take(level + 1).collect(), 83 | } 84 | } 85 | 86 | /// Consumes the node returning the item it contains. 87 | pub fn into_inner(mut self) -> Option { 88 | self.item.take() 89 | } 90 | 91 | /// Returns `true` is the node is a head-node. 92 | pub fn is_head(&self) -> bool { 93 | self.prev.is_none() 94 | } 95 | 96 | pub fn next_ref(&self) -> Option<&Self> { 97 | // SAFETY: all links either points to something or is null. 98 | unsafe { self.links[0].as_ref().map(|p| p.as_ref()) } 99 | } 100 | 101 | pub fn next_mut(&mut self) -> Option<&mut Self> { 102 | // SAFETY: all links either points to something or is null. 103 | unsafe { self.links[0].as_mut().map(|p| p.as_mut()) } 104 | } 105 | 106 | /// Takes the next node and set next_node.prev as null. 107 | /// 108 | /// SAFETY: please make sure no link at level 1 or greater becomes dangling. 109 | pub unsafe fn take_tail(&mut self) -> Option> { 110 | unsafe { 111 | self.links[0].take().map(|p| { 112 | let mut next = Box::from_raw(p.as_ptr()); 113 | next.prev = None; 114 | self.links_len[0] = 0; 115 | next 116 | }) 117 | } 118 | } 119 | 120 | /// Replace the next node. 121 | /// Return the old node. 122 | /// 123 | /// SAFETY: please makes sure all links are fixed. 124 | pub unsafe fn replace_tail(&mut self, mut new_next: Box) -> Option> { 125 | unsafe { 126 | let mut old_next = self.take_tail(); 127 | if let Some(old_next) = old_next.as_mut() { 128 | old_next.prev = None; 129 | } 130 | new_next.prev = Some(NonNull::new_unchecked(self as *mut _)); 131 | self.links[0] = Some(NonNull::new_unchecked(Box::into_raw(new_next))); 132 | self.links_len[0] = 1; 133 | old_next 134 | } 135 | } 136 | // ///////////////////////////// 137 | // Value Manipulation 138 | // ///////////////////////////// 139 | // 140 | // Methods that care about items carried by the nodes. 141 | 142 | /// Retain all nodes who satisfies `pred`. Return the number removed nodes.. 143 | /// 144 | /// Requires `self` being the head of the skiplist. 145 | /// 146 | /// `pred` is a function that takes two parameters, `Option<&V>` and `&V`. 147 | /// `Option<&V>` is the value of current node (`None` if the current node is the head), 148 | /// `&V` is the value of the next node. 149 | /// If the `pred` returns `false`, then the next node is dropped. 150 | #[must_use] 151 | pub fn retain(&mut self, mut pred: F) -> usize 152 | where 153 | F: FnMut(Option<&V>, &V) -> bool, 154 | { 155 | assert!(self.is_head()); 156 | let mut removed_count = 0; 157 | // Aliasing mutable references is undefined behavior. 158 | // However if you create a pointer from a mutable reference, 159 | // it essentially borrows from it, we are free to alias it until 160 | // the next time we use that reference. 161 | let mut current_node = self as *mut Self; 162 | // `level_heads` records every head of the linked list. 163 | // A head is the last node of a given level that is not after current_node. 164 | let mut level_heads: Vec<_> = iter::repeat(current_node).take(self.level + 1).collect(); 165 | // SAFETY: a huge block of pointer manipulation. 166 | unsafe { 167 | while let Some(mut next_node) = (*current_node).take_tail() { 168 | // next_node is removed from the list, so we can refer it by value. 169 | if pred( 170 | (*current_node).item.as_ref(), 171 | next_node.item.as_ref().unwrap(), 172 | ) { 173 | // Keeping next_node. 174 | // First we should update level_heads, then we put next_node back to the list. 175 | for x in &mut level_heads[0..=next_node.level] { 176 | *x = next_node.as_mut() as *mut _; 177 | } 178 | (*current_node).replace_tail(next_node); 179 | current_node = (*current_node).next_mut().unwrap(); 180 | } else { 181 | // Remove next_node. 182 | removed_count += 1; 183 | // Fixes links above level 0. 184 | for (level, head) in level_heads 185 | .iter_mut() 186 | .map(|&mut node_p| &mut *node_p) 187 | .enumerate() 188 | .skip(1) 189 | { 190 | if level <= next_node.level { 191 | assert!(ptr::eq( 192 | head.links[level].unwrap().as_ptr(), 193 | next_node.as_mut() 194 | )); 195 | head.links_len[level] += next_node.links_len[level]; 196 | head.links_len[level] -= 1; 197 | head.links[level] = next_node.links[level]; 198 | } else { 199 | head.links_len[level] -= 1; 200 | } 201 | } 202 | // Fix the link at level 0. 203 | if let Some(new_next) = next_node.take_tail() { 204 | (*current_node).replace_tail(new_next); 205 | } 206 | } 207 | } 208 | } 209 | removed_count 210 | } 211 | 212 | // ///////////////////////////// 213 | // Pointer Manipulations 214 | // ///////////////////////////// 215 | // 216 | // Methods that care about the whole node. 217 | // 218 | 219 | /// Distance between current node and the given node at specified level. 220 | /// If no node is given, then return distance between current node and the 221 | /// last possible node. 222 | /// If the node is not reachable on given level, return Err(()). 223 | pub fn distance_at_level(&self, level: usize, target: Option<&Self>) -> Result { 224 | let distance = match target { 225 | Some(target) => { 226 | let (dest, distance) = 227 | self.advance_while_at_level(level, |current, _| !ptr::eq(current, target)); 228 | if !ptr::eq(dest, target) { 229 | return Err(()); 230 | } 231 | distance 232 | } 233 | None => { 234 | let (dest, distance) = self.advance_while_at_level(level, |_, _| true); 235 | dest.links_len[level] + distance 236 | } 237 | }; 238 | Ok(distance) 239 | } 240 | 241 | /// Move for max_distance units. 242 | /// Returns None if it's not possible. 243 | pub fn advance(&self, max_distance: usize) -> Option<&Self> { 244 | let level = self.level; 245 | let mut node = self; 246 | let mut distance_left = max_distance; 247 | for level in (0..=level).rev() { 248 | let (new_node, steps) = node.advance_at_level(level, distance_left); 249 | distance_left -= steps; 250 | node = new_node; 251 | } 252 | if distance_left == 0 { Some(node) } else { None } 253 | } 254 | 255 | /// Move for max_distance units. 256 | /// Returns None if it's not possible. 257 | pub fn advance_mut(&mut self, max_distance: usize) -> Option<&mut Self> { 258 | let level = self.level; 259 | let mut node = self; 260 | let mut distance_left = max_distance; 261 | for level in (0..=level).rev() { 262 | let (new_node, steps) = node.advance_at_level_mut(level, distance_left); 263 | distance_left -= steps; 264 | node = new_node; 265 | } 266 | if distance_left == 0 { Some(node) } else { None } 267 | } 268 | 269 | /// Move to the last node reachable from this node. 270 | pub fn last(&self) -> &Self { 271 | (0..=self.level).rev().fold(self, |node, level| { 272 | node.advance_while_at_level(level, |_, _| true).0 273 | }) 274 | } 275 | 276 | /// Move to the last node reachable from this node. 277 | pub fn last_mut(&mut self) -> &mut Self { 278 | (0..=self.level).rev().fold(self, |node, level| { 279 | node.advance_while_at_level_mut(level, |_, _| true).0 280 | }) 281 | } 282 | 283 | /// Try to move for the given distance, only using links at the specified level. 284 | /// If it's impossible, then move as far as possible. 285 | /// 286 | /// Returns a reference to the new node and the distance travelled. 287 | pub fn advance_at_level(&self, level: usize, mut max_distance: usize) -> (&Self, usize) { 288 | self.advance_while_at_level(level, move |current_node, _| { 289 | let travelled = current_node.links_len[level]; 290 | if travelled <= max_distance { 291 | max_distance -= travelled; 292 | true 293 | } else { 294 | false 295 | } 296 | }) 297 | } 298 | 299 | /// Try to move for the given distance, only using links at the specified level. 300 | /// If it's impossible, then move as far as possible. 301 | /// 302 | /// Returns a mutable reference to the new node and the distance travelled. 303 | pub fn advance_at_level_mut( 304 | &mut self, 305 | level: usize, 306 | mut max_distance: usize, 307 | ) -> (&mut Self, usize) { 308 | self.advance_while_at_level_mut(level, move |current_node, _| { 309 | let travelled = current_node.links_len[level]; 310 | if travelled <= max_distance { 311 | max_distance -= travelled; 312 | true 313 | } else { 314 | false 315 | } 316 | }) 317 | } 318 | 319 | /// Keep moving at the specified level as long as pred is true. 320 | /// pred takes reference to current node and next node. 321 | pub fn advance_while_at_level( 322 | &self, 323 | level: usize, 324 | mut pred: impl FnMut(&Self, &Self) -> bool, 325 | ) -> (&Self, usize) { 326 | let mut current = self; 327 | let mut travelled = 0; 328 | loop { 329 | match current.next_if_at_level(level, &mut pred) { 330 | Ok((node, steps)) => { 331 | current = node; 332 | travelled += steps; 333 | } 334 | Err(node) => return (node, travelled), 335 | } 336 | } 337 | } 338 | 339 | /// Keep moving at the specified level as long as pred is true. 340 | /// pred takes reference to current node and next node. 341 | pub fn advance_while_at_level_mut( 342 | &mut self, 343 | level: usize, 344 | mut pred: impl FnMut(&Self, &Self) -> bool, 345 | ) -> (&mut Self, usize) { 346 | let mut current = self; 347 | let mut travelled = 0; 348 | loop { 349 | match current.next_if_at_level_mut(level, &mut pred) { 350 | Ok((node, steps)) => { 351 | current = node; 352 | travelled += steps; 353 | } 354 | Err(node) => return (node, travelled), 355 | } 356 | } 357 | } 358 | 359 | // The following methods return `Err(self)` if they fail. 360 | // 361 | // In Rust, the lifetime of returned value is the same as `self`. 362 | // Therefore if you return something that's borrowed from `self` in a branch, 363 | // `self` is considered borrowed in other branches. 364 | // 365 | // e.g. 366 | // ``` 367 | // fn some_method(&mut self) -> Option<&mut Self>; 368 | // 369 | // fn caller(&mut self) { 370 | // match self.some_method(){ 371 | // Some(x) => return x, // oops now `self` is borrowed until the function returns... 372 | // None => return self, // Now you cannot use `self` in other branches.. 373 | // } // including returning it! 374 | // } 375 | // ``` 376 | // While in this example you can restructure the code to fix that, 377 | // it's much more difficult when loops are involved. 378 | // The following methods are usually used in loops, so they return `Err(self)` 379 | // when they fail, to ease the pain. 380 | 381 | /// Move to the next node at given level if the given predicate is true. 382 | /// The predicate takes reference to the current node and the next node. 383 | pub fn next_if_at_level_mut( 384 | &mut self, 385 | level: usize, 386 | predicate: impl FnOnce(&Self, &Self) -> bool, 387 | ) -> Result<(&mut Self, usize), &mut Self> { 388 | // SAFETY: If a link contains Some(p), then p always points to something. 389 | let next = unsafe { self.links[level].and_then(|p| p.as_ptr().as_mut()) }; 390 | match next { 391 | Some(next) if predicate(self, next) => Ok((next, self.links_len[level])), 392 | _ => Err(self), 393 | } 394 | } 395 | 396 | /// Move to the next node at given level if the given predicate is true. 397 | /// The predicate takes reference to the current node and the next node. 398 | pub fn next_if_at_level( 399 | &self, 400 | level: usize, 401 | predicate: impl FnOnce(&Self, &Self) -> bool, 402 | ) -> Result<(&Self, usize), &Self> { 403 | // SAFETY: If a link contains Some(p), then p always points to something. 404 | let next = unsafe { self.links[level].as_ref().map(|p| p.as_ref()) }; 405 | match next { 406 | Some(next) if predicate(self, next) => Ok((next, self.links_len[level])), 407 | _ => Err(self), 408 | } 409 | } 410 | 411 | /// Insert a node after given distance after the list head. 412 | /// 413 | /// Requires that there's nothing before the node and the new node can't be at a higher level. 414 | /// 415 | /// Return the reference to the new node if successful. 416 | /// Give back the input node if not successful. 417 | pub fn insert_at( 418 | &mut self, 419 | new_node: Box, 420 | distance_to_parent: usize, 421 | ) -> Result<&mut Self, Box> { 422 | assert!(self.prev.is_none(), "Only the head may insert nodes!"); 423 | assert!( 424 | self.level >= new_node.level, 425 | "You may not insert nodes with level higher than the head!" 426 | ); 427 | let inserter = IndexInserter::new(distance_to_parent, new_node); 428 | inserter.act(self) 429 | } 430 | 431 | /// Move for distance units, and remove the node after it. 432 | /// 433 | /// Requires that there's nothing before the node and the new node can't be at a higher level. 434 | /// 435 | /// If that node exists, remove that node and return it. 436 | pub fn remove_at(&mut self, distance_to_parent: usize) -> Option> { 437 | assert!(self.prev.is_none(), "Only the head may remove nodes!"); 438 | let remover = IndexRemover::new(distance_to_parent); 439 | remover.act(self).ok() 440 | } 441 | 442 | /// Check the integrity of the list. 443 | /// 444 | pub fn check(&self) { 445 | assert!(self.is_head()); 446 | assert!(self.item.is_none()); 447 | let mut current_node = Some(self); 448 | let mut len = 0; 449 | while let Some(node) = current_node { 450 | // Check the integrity of node. 451 | assert_eq!(node.level + 1, node.links.len()); 452 | assert_eq!(node.level + 1, node.links_len.len()); 453 | if !node.is_head() { 454 | assert!(node.item.is_some()); 455 | } 456 | // Check link at level 0 457 | if let Some(next_node) = node.next_ref() { 458 | len += 1; 459 | assert!(ptr::eq(next_node.prev.unwrap().as_ptr(), node)); 460 | } 461 | current_node = node.next_ref(); 462 | } 463 | 464 | let len = len; // no mutation 465 | 466 | for lvl in 1..=self.level { 467 | let mut length_sum = 0; 468 | let mut current_node = Some(self); 469 | while let Some(node) = current_node { 470 | length_sum += node.links_len[lvl]; 471 | // SAFETY: all links are either None or should points to something. 472 | let next_node = unsafe { node.links[lvl].as_ref().map(|p| p.as_ref()) }; 473 | assert_eq!( 474 | node.links_len[lvl], 475 | node.distance_at_level(lvl - 1, next_node).unwrap(), 476 | "Node gives different distance at level {} and level {}!", 477 | lvl, 478 | lvl - 1 479 | ); 480 | 481 | current_node = next_node; 482 | } 483 | 484 | assert_eq!(length_sum, len); 485 | } 486 | } 487 | } 488 | 489 | impl Drop for SkipNode { 490 | fn drop(&mut self) { 491 | // SAFETY: all nodes are going to be dropped; its okay that its links (except those at 492 | // level 0) become dangling. 493 | unsafe { 494 | let mut node = self.take_tail(); 495 | while let Some(mut node_inner) = node { 496 | node = node_inner.take_tail(); 497 | } 498 | } 499 | } 500 | } 501 | 502 | // /////////////////////////////////////////////// 503 | // Trait implementation 504 | // /////////////////////////////////////////////// 505 | 506 | impl fmt::Display for SkipNode 507 | where 508 | V: fmt::Display, 509 | { 510 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 511 | match self.item { 512 | Some(ref v) => { 513 | write!(f, "{}", v) 514 | } 515 | _ => Ok(()), 516 | } 517 | } 518 | } 519 | 520 | // /////////////////////////////////////////////// 521 | // Actions 522 | // /////////////////////////////////////////////// 523 | 524 | /// A SeekListAction seeks a node on the list and do something, e.g. insertion, deletion, 525 | /// replacement, on it, often mutating the links in the process. 526 | /// 527 | /// Actions on skiplists usually consist of three phases: for each level, seek the node on which we modify the links, 528 | /// actually modify the link on the 0th level, and fixup the links at upper levels. 529 | /// 530 | /// Between the phases there are some bookkeeping, and this trait abstract that away. 531 | /// 532 | /// To use this trait, just implement the 3 phases (`seek`, `act_on_node`, `fixup`), 533 | /// and call `act()` on the given list. 534 | /// 535 | /// For examples, see one of the types that implements this trait, such as [IndexInserter] or [IndexRemover]. 536 | pub trait SkipListAction<'a, T>: Sized { 537 | /// Return type when this action succeeds. 538 | type Ok; 539 | /// Return type when this action fails. 540 | type Err; 541 | 542 | /// This is called when the list action has failed. 543 | /// 544 | /// This is only called when seek() finds nothing. 545 | fn fail(self) -> Self::Err; 546 | 547 | /// Find the target node at the given level. 548 | /// 549 | /// Return some node and distance travelled. 550 | /// Return `None` when target node does not exist anywhere in the list. 551 | /// In this case, the action is considered to have failed, and `Self::fail()` will be called. 552 | /// 553 | /// Target node may not exist at a higher level. 554 | /// You should return some node before the target node in this case. 555 | /// At level 0 it always finds the target or return `None`. 556 | fn seek( 557 | &mut self, 558 | node: &'a mut SkipNode, 559 | level: usize, 560 | ) -> Option<(&'a mut SkipNode, usize)>; 561 | 562 | /// Do something on the node. 563 | /// 564 | /// If this action fails, then `Self::fail()` will not be called. 565 | /// This makes error handling more flexible. 566 | /// # SAFETY 567 | /// If `Self::Ok` is a reference, it shall not alias with any node that needs fixup. 568 | unsafe fn act_on_node(self, node: &'a mut SkipNode) -> Result; 569 | 570 | /// Usually SkipListAction breaks links between nodes, this method should fix that up. 571 | /// 572 | /// It should never fail. 573 | /// 574 | /// `level_head` is the node whose links may needs to be fixed. 575 | /// `action_result` is a mutable reference to the return value of act_on_node(). 576 | /// `distance_to_target` is distance from `level_head` to the node that has been acted on. 577 | fn fixup( 578 | level: usize, 579 | level_head: &'a mut SkipNode, 580 | distance_to_target: usize, 581 | action_result: &mut Self::Ok, 582 | ); 583 | 584 | /// List traversal logic. 585 | /// 586 | /// This handles bookkeeping required for all skiplist mutations. 587 | /// 588 | /// It's unlikely one will need to override this. 589 | /// Override act() instead. 590 | unsafe fn _traverse( 591 | mut self, 592 | node: &'a mut SkipNode, 593 | level: usize, 594 | ) -> Result<(Self::Ok, usize), Self::Err> { 595 | unsafe { 596 | let (level_head, distance_this_level) = match self.seek(node, level) { 597 | Some(res) => res, 598 | None => return Err(self.fail()), 599 | }; 600 | let level_head_p = level_head as *mut SkipNode; 601 | if level == 0 { 602 | let mut res = self.act_on_node(level_head)?; 603 | Self::fixup(0, &mut *level_head_p, 0, &mut res); 604 | Ok((res, distance_this_level)) 605 | } else { 606 | let (mut res, distance_after_head) = self._traverse(level_head, level - 1)?; 607 | let level_head = &mut *level_head_p; 608 | Self::fixup(level, level_head, distance_after_head, &mut res); 609 | Ok((res, distance_this_level + distance_after_head)) 610 | } 611 | } 612 | } 613 | 614 | /// Perform the action. 615 | fn act(self, list_head: &'a mut SkipNode) -> Result { 616 | let (res, _distance) = unsafe { self._traverse(list_head, list_head.level)? }; 617 | Ok(res) 618 | } 619 | } 620 | 621 | // helpers for ListActions. 622 | impl SkipNode { 623 | /// Insert the new node immediately after this node. 624 | /// 625 | /// SAFETY: This doesn't fix links at level 1 or higher. 626 | pub unsafe fn insert_next(&mut self, mut new_node: Box>) -> &mut SkipNode { 627 | unsafe { 628 | if let Some(tail) = self.take_tail() { 629 | new_node.replace_tail(tail); 630 | } 631 | self.replace_tail(new_node); 632 | self.next_mut().unwrap() 633 | } 634 | } 635 | 636 | /// Take the node immediately after this node. 637 | /// 638 | /// SAFETY: This doesn't fix links at level 1 or higher. 639 | pub unsafe fn take_next(&mut self) -> Option>> { 640 | unsafe { 641 | let mut ret = self.take_tail()?; 642 | if let Some(new_tail) = ret.take_tail() { 643 | self.replace_tail(new_tail); 644 | } 645 | Some(ret) 646 | } 647 | } 648 | } 649 | 650 | /// Helper to seek the node at specific index. 651 | /// self.0 is the distance between current node and target. 652 | struct DistanceSeeker(usize); 653 | 654 | impl DistanceSeeker { 655 | /// Find the last node reachable from the given level 656 | /// whose distance from `node` is no greater than `self.0`. 657 | /// 658 | /// Return target node and distance travelled if succeeds. 659 | fn seek<'a, V>( 660 | &mut self, 661 | node: &'a mut SkipNode, 662 | level: usize, 663 | ) -> Option<(&'a mut SkipNode, usize)> { 664 | let (node, distance) = node.advance_at_level_mut(level, self.0); 665 | if level == 0 && distance != self.0 { 666 | None 667 | } else { 668 | self.0 -= distance; 669 | Some((node, distance)) 670 | } 671 | } 672 | } 673 | 674 | /// Insert a new node at the given index. 675 | /// 676 | /// See [SkipNode::insert_at] for examples on how to use. 677 | struct IndexInserter { 678 | index_seek: DistanceSeeker, 679 | new_node: Box>, 680 | } 681 | 682 | impl IndexInserter { 683 | fn new(distance: usize, new_node: Box>) -> Self { 684 | IndexInserter { 685 | index_seek: DistanceSeeker(distance), 686 | new_node, 687 | } 688 | } 689 | } 690 | 691 | impl<'a, V: 'a> SkipListAction<'a, V> for IndexInserter { 692 | type Ok = &'a mut SkipNode; 693 | 694 | type Err = Box>; 695 | 696 | /// Return the would-be-inserted node if fails. 697 | fn fail(self) -> Self::Err { 698 | self.new_node 699 | } 700 | 701 | /// Finds the parent of the new node. 702 | fn seek( 703 | &mut self, 704 | node: &'a mut SkipNode, 705 | level: usize, 706 | ) -> Option<(&'a mut SkipNode, usize)> { 707 | self.index_seek.seek(node, level) 708 | } 709 | 710 | /// SAFETY: This returns a new node, which should never alias with any old nodes. 711 | unsafe fn act_on_node(self, node: &'a mut SkipNode) -> Result { 712 | unsafe { 713 | // SAFETY: Links will be fixed by the caller. 714 | Ok(node.insert_next(self.new_node)) 715 | } 716 | } 717 | 718 | fn fixup( 719 | level: usize, 720 | level_head: &'a mut SkipNode, 721 | distance_to_parent: usize, 722 | new_node: &mut Self::Ok, 723 | ) { 724 | insertion_fixup(level, level_head, distance_to_parent, new_node) 725 | } 726 | } 727 | 728 | /// Remove the node at the given index. 729 | /// 730 | /// See [SkipNode::remove_at] for examples on how to use. 731 | struct IndexRemover { 732 | seeker: DistanceSeeker, 733 | } 734 | 735 | impl IndexRemover { 736 | fn new(distance: usize) -> Self { 737 | IndexRemover { 738 | seeker: DistanceSeeker(distance), 739 | } 740 | } 741 | } 742 | 743 | impl<'a, V> SkipListAction<'a, V> for IndexRemover { 744 | type Ok = Box>; 745 | 746 | type Err = (); 747 | 748 | /// The only way to fail is when `seek()` does not find an appropriate node, 749 | /// so we just do nothing. 750 | #[allow(clippy::unused_unit)] 751 | fn fail(self) -> Self::Err { 752 | () 753 | } 754 | 755 | fn seek( 756 | &mut self, 757 | node: &'a mut SkipNode, 758 | level: usize, 759 | ) -> Option<(&'a mut SkipNode, usize)> { 760 | self.seeker.seek(node, level) 761 | } 762 | 763 | // SAFETY: Self::Ok is not a reference type 764 | unsafe fn act_on_node(self, node: &'a mut SkipNode) -> Result { 765 | unsafe { 766 | // SAFETY: links will be fixed by the caller. 767 | node.take_next().ok_or(()) 768 | } 769 | } 770 | 771 | fn fixup( 772 | level: usize, 773 | level_head: &'a mut SkipNode, 774 | _distance_to_parent: usize, 775 | removed_node: &mut Self::Ok, 776 | ) { 777 | removal_fixup(level, level_head, removed_node) 778 | } 779 | } 780 | 781 | /// Fixes links at `level` after insertion. 782 | /// 783 | /// Put the new_node after level_head if applicable, and adjust link_len. 784 | /// `distance_to_parent` is the distance from `level_head` to the parent of `new_node`. 785 | pub fn insertion_fixup( 786 | level: usize, 787 | level_head: &mut SkipNode, 788 | distance_to_parent: usize, 789 | new_node: &mut &mut SkipNode, 790 | ) { 791 | if level == 0 { 792 | // Already handled by insertion. 793 | return; 794 | } 795 | if level <= new_node.level { 796 | new_node.links[level] = level_head.links[level]; 797 | level_head.links[level] = NonNull::new(*new_node); 798 | let old_len = level_head.links_len[level]; 799 | new_node.links_len[level] = old_len - distance_to_parent; 800 | level_head.links_len[level] = distance_to_parent + 1; 801 | } else { 802 | level_head.links_len[level] += 1; 803 | } 804 | } 805 | 806 | /// Fix links at the given level after removal. 807 | pub fn removal_fixup( 808 | level: usize, 809 | level_head: &mut SkipNode, 810 | removed_node: &mut Box>, 811 | ) { 812 | if level == 0 { 813 | return; 814 | } 815 | if level <= removed_node.level { 816 | assert!(ptr::eq( 817 | level_head.links[level].unwrap().as_ptr(), 818 | removed_node.as_ref() 819 | )); 820 | level_head.links[level] = removed_node.links[level]; 821 | level_head.links_len[level] += removed_node.links_len[level]; 822 | } 823 | level_head.links_len[level] -= 1; 824 | } 825 | 826 | // helpers for ordered types. 827 | impl SkipNode { 828 | /// Find the last node such that f(node.item) returns true. 829 | /// Return a reference to the node and distance travelled. 830 | fn find_ordering_impl(&self, f: F) -> (&Self, usize) 831 | where 832 | F: Fn(&V) -> bool, 833 | { 834 | (0..=self.level) 835 | .rev() 836 | .fold((self, 0), |(node, distance), level| { 837 | let (node, steps) = node.advance_while_at_level(level, |_, next_node| { 838 | let value = next_node.item.as_ref().unwrap(); 839 | f(value) 840 | }); 841 | (node, distance + steps) 842 | }) 843 | } 844 | 845 | /// Find the last node such that f(node.item) returns true. 846 | /// Return a mutable reference to the node and distance travelled. 847 | fn find_ordering_mut_impl(&mut self, f: F) -> (&mut Self, usize) 848 | where 849 | F: Fn(&V) -> bool, 850 | { 851 | (0..=self.level) 852 | .rev() 853 | .fold((self, 0), |(node, distance), level| { 854 | let (node, steps) = node.advance_while_at_level_mut(level, |_, next_node| { 855 | let value = next_node.item.as_ref().unwrap(); 856 | f(value) 857 | }); 858 | (node, distance + steps) 859 | }) 860 | } 861 | 862 | /// Given a list head, a comparison function and a target, 863 | /// return a reference to the last node whose item compares less than the target, 864 | /// and the distance to that node. 865 | pub fn find_last_le_with(&self, cmp: F, target: &T) -> (&Self, usize) 866 | where 867 | F: Fn(&V, &T) -> Ordering, 868 | { 869 | self.find_ordering_impl(|node_value| cmp(node_value, target) != Ordering::Greater) 870 | } 871 | 872 | /// Given a list head, a comparison function and a target, 873 | /// return a mutable reference to the last node whose item compares less than the target. 874 | /// and the distance to that node. 875 | pub fn find_last_le_with_mut(&mut self, cmp: F, target: &T) -> (&mut Self, usize) 876 | where 877 | F: Fn(&V, &T) -> Ordering, 878 | { 879 | self.find_ordering_mut_impl(|node_value| cmp(node_value, target) != Ordering::Greater) 880 | } 881 | 882 | /// Given a list head, a comparison function and a target, 883 | /// return a reference to the last node whose item compares less than or equal to the target. 884 | /// and the distance to that node. 885 | pub fn find_last_lt_with(&self, cmp: F, target: &T) -> (&Self, usize) 886 | where 887 | F: Fn(&V, &T) -> Ordering, 888 | { 889 | assert!(self.is_head()); 890 | self.find_ordering_impl(|node_value| cmp(node_value, target) == Ordering::Less) 891 | } 892 | 893 | /// Given a list head, a comparison function and a target, 894 | /// return a mutable refeerence to the last node whose item compares less than or equal to the target. 895 | /// and the distance to that node. 896 | #[allow(dead_code)] 897 | pub fn find_last_lt_with_mut(&mut self, cmp: F, target: &T) -> (&mut Self, usize) 898 | where 899 | F: Fn(&V, &T) -> Ordering, 900 | { 901 | assert!(self.is_head()); 902 | self.find_ordering_mut_impl(|node_value| cmp(node_value, target) == Ordering::Less) 903 | } 904 | } 905 | 906 | // /////////////////////////////////////////////// 907 | // Helper Traits 908 | // /////////////////////////////////////////////// 909 | 910 | // Converting Option<&T> to *_ T becomes more and more annoying... 911 | trait AsPtr { 912 | fn as_ptr(&self) -> *const T; 913 | } 914 | 915 | trait AsPtrMut { 916 | fn as_ptr_mut(&mut self) -> *mut T; 917 | } 918 | 919 | impl AsPtr for Option<&T> { 920 | fn as_ptr(&self) -> *const T { 921 | self.map_or(ptr::null(), |inner_ref| inner_ref) 922 | } 923 | } 924 | 925 | impl AsPtr for Option<&mut T> { 926 | fn as_ptr(&self) -> *const T { 927 | self.as_ref().map_or(ptr::null(), |inner: &&mut T| &**inner) 928 | } 929 | } 930 | 931 | impl AsPtrMut for Option<&mut T> { 932 | fn as_ptr_mut(&mut self) -> *mut T { 933 | self.as_mut() 934 | .map_or(ptr::null_mut(), |inner: &mut &mut T| *inner) 935 | } 936 | } 937 | 938 | // ///////////////////////////////// 939 | // Iterators 940 | // ///////////////////////////////// 941 | // Since Iterators (currently) only pop from front and back, 942 | // they can be shared by some data structures. 943 | // There's no need for a dummy head (that contains no item) in the iterator. 944 | // so the members are named first and last instead of head/end to avoid confusion. 945 | 946 | /// An iterator for [SkipList](super::SkipList) and [OrderedSkipList](super::OrderedSkipList). 947 | pub struct Iter<'a, T> { 948 | pub(crate) first: Option<&'a SkipNode>, 949 | pub(crate) last: Option<&'a SkipNode>, 950 | pub(crate) size: usize, 951 | } 952 | impl<'a, T> Iter<'a, T> { 953 | /// SAFETY: There must be `len` nodes after head. 954 | pub(crate) unsafe fn from_head(head: &'a SkipNode, len: usize) -> Self { 955 | if len == 0 { 956 | Iter { 957 | first: None, 958 | last: None, 959 | size: 0, 960 | } 961 | } else { 962 | let first = head.next_ref(); 963 | let last = first.as_ref().map(|n| n.last()); 964 | Iter { 965 | first, 966 | last, 967 | size: len, 968 | } 969 | } 970 | } 971 | } 972 | 973 | impl<'a, T> Iterator for Iter<'a, T> { 974 | type Item = &'a T; 975 | 976 | fn next(&mut self) -> Option { 977 | let current_node = self.first?; 978 | if ptr::eq(current_node, self.last.as_ptr()) { 979 | self.first = None; 980 | self.last = None; 981 | } else { 982 | self.first = current_node.next_ref(); 983 | } 984 | self.size -= 1; 985 | current_node.item.as_ref() 986 | } 987 | 988 | fn size_hint(&self) -> (usize, Option) { 989 | (self.size, Some(self.size)) 990 | } 991 | } 992 | 993 | impl DoubleEndedIterator for Iter<'_, T> { 994 | fn next_back(&mut self) -> Option { 995 | let last_node = self.last?; 996 | 997 | if ptr::eq(self.first.as_ptr(), last_node) { 998 | self.first = None; 999 | self.last = None; 1000 | } else { 1001 | // SAFETY: The iterator is not empty yet. 1002 | unsafe { 1003 | self.last = last_node.prev.as_ref().map(|p| p.as_ref()); 1004 | } 1005 | } 1006 | self.size -= 1; 1007 | last_node.item.as_ref() 1008 | } 1009 | } 1010 | 1011 | /// A mutable iterator for [SkipList](super::SkipList) and [OrderedSkipList](super::OrderedSkipList). 1012 | pub struct IterMut<'a, T> { 1013 | pub(crate) first: Option<&'a mut SkipNode>, 1014 | pub(crate) last: Option>>, 1015 | pub(crate) size: usize, 1016 | } 1017 | 1018 | impl<'a, T> IterMut<'a, T> { 1019 | /// SAFETY: There must be `len` nodes after head. 1020 | pub(crate) unsafe fn from_head(head: &'a mut SkipNode, len: usize) -> Self { 1021 | if len == 0 { 1022 | IterMut { 1023 | first: None, 1024 | last: None, 1025 | size: 0, 1026 | } 1027 | } else { 1028 | let last = NonNull::new(head.last_mut()); 1029 | let first = head.next_mut(); 1030 | IterMut { 1031 | first, 1032 | last, 1033 | size: len, 1034 | } 1035 | } 1036 | } 1037 | } 1038 | 1039 | impl<'a, T> Iterator for IterMut<'a, T> { 1040 | type Item = &'a mut T; 1041 | 1042 | fn next(&mut self) -> Option { 1043 | let current_node = self.first.take()?; 1044 | if ptr::eq(current_node, self.last.unwrap().as_ptr()) { 1045 | self.first = None; 1046 | self.last = None; 1047 | } else { 1048 | // calling current_node.next_mut() borrows it, transforming the reference to a pointer 1049 | // unborrows that. 1050 | let p = current_node.next_mut().unwrap() as *mut SkipNode; 1051 | // SAFETY: p.as_mut() is safe because it points to a valid object. 1052 | // There's no aliasing issue since nobody else holds a reference to current_node 1053 | // until this function returns, and the returned reference does not points to a node. 1054 | unsafe { 1055 | self.first = p.as_mut(); 1056 | } 1057 | } 1058 | self.size -= 1; 1059 | current_node.item.as_mut() 1060 | } 1061 | 1062 | fn size_hint(&self) -> (usize, Option) { 1063 | (self.size, Some(self.size)) 1064 | } 1065 | } 1066 | 1067 | impl DoubleEndedIterator for IterMut<'_, T> { 1068 | fn next_back(&mut self) -> Option { 1069 | self.last?; 1070 | debug_assert!(self.last.is_some()); 1071 | // There can be at most one mutable reference to the first node. 1072 | // We need to take it from self.first before doing anything, 1073 | // including simple comparison. 1074 | let first = self.first.take().unwrap(); 1075 | let popped = if ptr::eq(first, self.last.unwrap().as_ptr()) { 1076 | self.last = None; 1077 | first 1078 | } else { 1079 | // SAFETY: self.last isn't null and doesn't alias first 1080 | let new_last = unsafe { self.last.unwrap().as_mut().prev }; 1081 | if ptr::eq(first, new_last.unwrap().as_ptr()) { 1082 | self.last = new_last; 1083 | let popped_p = first.next_mut().unwrap() as *mut SkipNode; 1084 | self.first.replace(first); 1085 | unsafe { &mut (*popped_p) } 1086 | } else { 1087 | self.first.replace(first); 1088 | let last = self.last; 1089 | self.last = new_last; 1090 | unsafe { last.unwrap().as_ptr().as_mut().unwrap() } 1091 | } 1092 | }; 1093 | self.size -= 1; 1094 | popped.item.as_mut() 1095 | } 1096 | } 1097 | 1098 | /// Consuming iterator for [SkipList](super::SkipList), [OrderedSkipList](super::OrderedSkipList) and [SkipMap](super::SkipMap). 1099 | pub struct IntoIter { 1100 | pub(crate) first: Option>>, 1101 | pub(crate) last: Option>>, 1102 | pub(crate) size: usize, 1103 | } 1104 | 1105 | impl IntoIter { 1106 | /// SAFETY: There must be `len` nodes after head. 1107 | pub(crate) unsafe fn from_head(head: &mut SkipNode, len: usize) -> Self { 1108 | unsafe { 1109 | if len == 0 { 1110 | IntoIter { 1111 | first: None, 1112 | last: None, 1113 | size: 0, 1114 | } 1115 | } else { 1116 | let last = NonNull::new(head.last_mut()); 1117 | let first = head.take_tail(); 1118 | IntoIter { 1119 | first, 1120 | last, 1121 | size: len, 1122 | } 1123 | } 1124 | } 1125 | } 1126 | } 1127 | 1128 | impl Iterator for IntoIter { 1129 | type Item = T; 1130 | 1131 | fn next(&mut self) -> Option { 1132 | let mut popped_node = self.first.take()?; 1133 | self.size -= 1; 1134 | // SAFETY: no need to fix links at upper levels inside iterators. 1135 | self.first = unsafe { popped_node.take_tail() }; 1136 | if self.first.is_none() { 1137 | self.last = None; 1138 | } 1139 | popped_node.into_inner() 1140 | } 1141 | 1142 | fn size_hint(&self) -> (usize, Option) { 1143 | (self.size, Some(self.size)) 1144 | } 1145 | } 1146 | 1147 | impl DoubleEndedIterator for IntoIter { 1148 | fn next_back(&mut self) -> Option { 1149 | #[allow(clippy::question_mark)] 1150 | if self.first.is_none() { 1151 | return None; 1152 | } 1153 | assert!( 1154 | self.last.is_some(), 1155 | "The IntoIter should be empty but IntoIter.last somehow still contains something" 1156 | ); 1157 | let popped_node = if ptr::eq(self.first.as_deref().as_ptr(), self.last.unwrap().as_ptr()) { 1158 | self.last = None; 1159 | self.first.take()? 1160 | } else { 1161 | // SAFETY: we checked that self.last points to somewhere and does not alias to self.first 1162 | let new_last = unsafe { self.last.unwrap().as_mut().prev }; 1163 | if ptr::eq(self.first.as_deref().as_ptr(), new_last.unwrap().as_ptr()) { 1164 | // SAFETY: take_tail() is always safe in IntoIter. 1165 | let popped = unsafe { 1166 | self.first 1167 | .as_mut() 1168 | .and_then(|node| node.take_tail()) 1169 | .unwrap() 1170 | }; 1171 | self.last = new_last; 1172 | popped 1173 | } else { 1174 | // SAFETY: we checked new_last points to somewhere and do not alias to self.first. 1175 | let popped = unsafe { new_last.unwrap().as_mut().take_tail().unwrap() }; 1176 | self.last = new_last; 1177 | popped 1178 | } 1179 | }; 1180 | 1181 | self.size -= 1; 1182 | popped_node.into_inner() 1183 | } 1184 | } 1185 | 1186 | #[cfg(test)] 1187 | mod test { 1188 | use super::*; 1189 | 1190 | /// Minimum levels required for a list of size n. 1191 | fn levels_required(n: usize) -> usize { 1192 | if n == 0 { 1193 | 1 1194 | } else { 1195 | let num_bits = std::mem::size_of::() * 8; 1196 | num_bits - n.leading_zeros() as usize 1197 | } 1198 | } 1199 | 1200 | /// Test test_covariance for SkipNode. 1201 | /// Those functions should compile if our data structures is covariant. 1202 | /// Read Rustonomicon for details. 1203 | #[test] 1204 | fn test_covariance() { 1205 | #[allow(dead_code)] 1206 | fn shorten_lifetime<'min, 'max: 'min>(v: SkipNode<&'max ()>) -> SkipNode<&'min ()> { 1207 | v 1208 | } 1209 | 1210 | #[allow(dead_code)] 1211 | fn shorten_lifetime_into_iter<'min, 'max: 'min>( 1212 | v: IntoIter<&'max ()>, 1213 | ) -> IntoIter<&'min ()> { 1214 | v 1215 | } 1216 | 1217 | // IterMut is covariant on the value type. 1218 | // This is consistent with Rust reference &'a T. 1219 | #[allow(dead_code)] 1220 | fn shorten_lifetime_iter<'min, 'max: 'min>( 1221 | v: Iter<'max, &'max ()>, 1222 | ) -> Iter<'min, &'min ()> { 1223 | v 1224 | } 1225 | 1226 | // IterMut is not covariant on the value type. 1227 | // This is consistent with Rust mutable reference type &mut T. 1228 | // TODO: write a test that can't compile 1229 | #[allow(dead_code)] 1230 | fn shorten_lifetime_iter_mut<'min, 'max: 'min>(v: Iter<'max, ()>) -> Iter<'min, ()> { 1231 | v 1232 | } 1233 | } 1234 | 1235 | #[test] 1236 | fn test_level_required() { 1237 | assert_eq!(levels_required(0), 1); 1238 | assert_eq!(levels_required(1), 1); 1239 | assert_eq!(levels_required(2), 2); 1240 | assert_eq!(levels_required(3), 2); 1241 | assert_eq!(levels_required(1023), 10); 1242 | assert_eq!(levels_required(1024), 11); 1243 | } 1244 | 1245 | fn level_for_index(mut n: usize) -> usize { 1246 | let mut cnt = 0; 1247 | while n & 0x1 == 1 { 1248 | cnt += 1; 1249 | n /= 2; 1250 | } 1251 | cnt 1252 | } 1253 | 1254 | #[test] 1255 | fn test_level_index() { 1256 | assert_eq!(level_for_index(0), 0); 1257 | assert_eq!(level_for_index(1), 1); 1258 | assert_eq!(level_for_index(2), 0); 1259 | assert_eq!(level_for_index(3), 2); 1260 | assert_eq!(level_for_index(4), 0); 1261 | assert_eq!(level_for_index(5), 1); 1262 | assert_eq!(level_for_index(6), 0); 1263 | assert_eq!(level_for_index(7), 3); 1264 | assert_eq!(level_for_index(8), 0); 1265 | assert_eq!(level_for_index(9), 1); 1266 | assert_eq!(level_for_index(10), 0); 1267 | assert_eq!(level_for_index(11), 2); 1268 | } 1269 | 1270 | /// Make a list of size n 1271 | /// levels are evenly spread out 1272 | fn new_list_for_test(n: usize) -> Box> { 1273 | let max_level = levels_required(n); 1274 | let mut head = Box::new(SkipNode::::head(max_level)); 1275 | assert_eq!(head.links.len(), max_level); 1276 | let mut nodes: Vec<_> = (0..n) 1277 | .map(|n| { 1278 | let new_node = Box::new(SkipNode::new(n, level_for_index(n))); 1279 | Box::into_raw(new_node) 1280 | }) 1281 | .collect(); 1282 | unsafe { 1283 | let node_max_level = nodes.iter().map(|&node| (*node).level).max(); 1284 | if let Some(node_max_level) = node_max_level { 1285 | assert_eq!(node_max_level + 1, max_level); 1286 | } 1287 | for level in 0..max_level { 1288 | let mut last_node = head.as_mut() as *mut SkipNode; 1289 | let mut len_left = n; 1290 | for &mut node_ptr in nodes 1291 | .iter_mut() 1292 | .filter(|&&mut node_ptr| level <= (*node_ptr).level) 1293 | { 1294 | if level == 0 { 1295 | (*node_ptr).prev = NonNull::new(last_node); 1296 | } 1297 | (*last_node).links[level] = NonNull::new(node_ptr); 1298 | (*last_node).links_len[level] = 1 << level; 1299 | last_node = node_ptr; 1300 | len_left -= 1 << level; 1301 | } 1302 | (*last_node).links_len[level] = len_left; 1303 | } 1304 | } 1305 | head 1306 | } 1307 | 1308 | ///////////////////////////////////////////////////////// 1309 | // Those tests are supposed to be run using Miri to detect UB. 1310 | // The size of those test are limited since Miri doesn't run very fast. 1311 | ///////////////////////////////////////////////////////// 1312 | 1313 | #[test] 1314 | fn miri_test_insert() { 1315 | let mut list = new_list_for_test(50); 1316 | list.insert_at(Box::new(SkipNode::new(100, 0)), 25).unwrap(); 1317 | list.insert_at(Box::new(SkipNode::new(101, 1)), 25).unwrap(); 1318 | list.insert_at(Box::new(SkipNode::new(102, 2)), 25).unwrap(); 1319 | list.insert_at(Box::new(SkipNode::new(103, 3)), 25).unwrap(); 1320 | list.insert_at(Box::new(SkipNode::new(104, 4)), 25).unwrap(); 1321 | } 1322 | 1323 | #[test] 1324 | fn miri_test_remove() { 1325 | let mut list = new_list_for_test(50); 1326 | for i in (0..50).rev() { 1327 | list.remove_at(i).unwrap(); 1328 | } 1329 | } 1330 | 1331 | #[test] 1332 | fn miri_test_distance() { 1333 | let list = new_list_for_test(50); 1334 | for i in 0..=list.level { 1335 | let _ = list.distance_at_level(i, None); 1336 | } 1337 | } 1338 | 1339 | #[test] 1340 | fn miri_test_iter() { 1341 | fn test_iter(size: usize) { 1342 | let list = new_list_for_test(size); 1343 | let first = list.next_ref(); 1344 | let last = Some(list.last()); 1345 | let mut iter = Iter { first, last, size }; 1346 | for _ in 0..(size + 1) / 2 { 1347 | let _ = iter.next(); 1348 | let _ = iter.next_back(); 1349 | } 1350 | assert!(iter.next().is_none()); 1351 | } 1352 | test_iter(9); 1353 | test_iter(10); 1354 | } 1355 | 1356 | #[test] 1357 | fn miri_test_iter_mut() { 1358 | fn test_iter_mut(size: usize) { 1359 | let mut list = new_list_for_test(size); 1360 | let mut first = list.next_mut(); 1361 | let last = first.as_mut().unwrap().last_mut(); 1362 | let last = NonNull::new(last); 1363 | let mut iter = IterMut { first, last, size }; 1364 | for _ in 0..(size + 1) / 2 { 1365 | let _ = iter.next(); 1366 | let _ = iter.next_back(); 1367 | } 1368 | assert!(iter.next().is_none()); 1369 | } 1370 | test_iter_mut(9); 1371 | test_iter_mut(10); 1372 | } 1373 | 1374 | #[test] 1375 | fn miri_test_into_iter() { 1376 | fn test_into_iter(size: usize) { 1377 | let mut list = new_list_for_test(size); 1378 | let mut first = unsafe { Some(list.take_tail().unwrap()) }; 1379 | let last = first.as_mut().unwrap().last_mut(); 1380 | let last = NonNull::new(last); 1381 | let mut iter = IntoIter { first, last, size }; 1382 | for _ in 0..(size + 1) / 2 { 1383 | let _ = iter.next(); 1384 | let _ = iter.next_back(); 1385 | } 1386 | assert!(iter.next().is_none()); 1387 | } 1388 | 1389 | test_into_iter(9); 1390 | test_into_iter(10); 1391 | } 1392 | 1393 | #[test] 1394 | fn miri_test_retain() { 1395 | let mut list = new_list_for_test(50); 1396 | let _ = list.retain(|_, val| val % 2 == 0); 1397 | } 1398 | 1399 | #[test] 1400 | fn miri_test_check() { 1401 | let list = new_list_for_test(100); 1402 | list.check(); 1403 | } 1404 | } 1405 | --------------------------------------------------------------------------------