├── .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 | [](https://crates.io/crates/skiplist)
4 | [](https://crates.io/crates/skiplist)
5 | [](https://codecov.io/gh/JP-Ellis/rust-skiplist)
6 | [](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