# splaylist
[](https://crates.io/crates/splaylist)
[](https://docs.rs/splaylist)
[](https://codeberg.org/gregburd/skiplist/actions)


A **self-balancing skip list** for Rust: an ordered map (`SplayMap`) and set
(`SplaySet`) whose node heights adapt to the access pattern, so frequently
used keys are promoted to taller towers and become cheaper to reach.
A skip list keeps keys in a sorted linked list with stacked "express lane"
forward pointers, giving `O(log n)` search, insert, and remove on average. A
*splay list* (Aksenov et al., 2020) adds adaptivity: each node counts how often
it is accessed, and the structure periodically reshapes itself so the expected
search cost tracks the *entropy* of the access distribution rather than a flat
`log n`. In practice that lowers splaylist's *own* cost on skewed reads; it does
not make it faster than `BTreeMap` (see [Benchmarks](#benchmarks)).
This crate is the safe-Rust descendant of a C `sl.h` splay list. It contains
**no `unsafe` code** (`#![forbid(unsafe_code)]`): nodes live in a single arena
`Vec` and links are array indices, so there are no raw pointers, no manual
lifetimes, and no memory-reclamation hazards.
```toml
[dependencies]
splaylist = "0.1"
```
## Example
```rust
use splaylist::SplayMap;
let mut map = SplayMap::new();
map.insert(3, "three");
map.insert(1, "one");
map.insert(2, "two");
assert_eq!(map.get(&2), Some(&"two"));
// Always iterates in ascending key order, however the towers have adapted.
let keys: Vec<_> = map.keys().copied().collect();
assert_eq!(keys, [1, 2, 3]);
// Entry API, ranges, and the usual collection traits are all available.
*map.entry(1).or_insert("?") = "ONE";
Every `get` records an access against the node it finds (a cheap interior
mutation through a `Cell`). Structural reshaping — promoting hot nodes and
demoting cold ones by one level at a time toward the target height
`K − 1 − log2(total / hits)` — happens during mutating operations on a fixed
interval, and can be forced with `SplayMap::rebalance`. The interval is set via
`SplayMap::with_config`; `splay_interval: 0` disables adaptation entirely,
leaving a plain randomized skip list.
The seed is configurable too, so a map's structural evolution is fully
deterministic for tests and reproducible benchmarks.
## Correctness and testing
- `#![forbid(unsafe_code)]` — the whole crate is safe Rust.
- Property-based tests use [Hegel](https://docs.rs/hegeltest) (Hypothesis
under the hood). A stateful model test drives `SplayMap`/`SplaySet` through
random operation sequences against `BTreeMap`/`BTreeSet` oracles and asserts
agreement after every step (in default, aggressive, and disabled adaptation
modes), plus range and `into_iter` equivalence. They live in `hegel-tests/`
(a separate crate, since `hegeltest` needs a Hypothesis server); run with
`cd hegel-tests && cargo test`.
- Example-based integration and unit tests run on every `cargo test` and are
self-contained (no server required).
- Internal unit tests assert the structural invariants (level 0 is a complete,
strictly-ascending, doubly-linked list; every upper level is a sorted
subsequence) hold under churn, that hot keys end up taller than cold ones,
and — as a regression guard — that heavy adaptation never collapses search
cost below `O(log n)`.
- A `cargo fuzz` differential target lives in `fuzz/`.
- `cargo miri test` is clean (no undefined behaviour), and `cargo deny` guards
the dependency tree.
- Comparison benchmarks against other Rust ordered maps live in `comparison/`
(see [Benchmarks](#benchmarks)).
## Minimum supported Rust version
The MSRV is **1.70**. Raising it is a minor-version change.
## License
Licensed under either of [Apache-2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT) at
your option. The C ancestor is dual-licensed ISC OR MIT.
[`std::collections::BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
[`crossbeam-skiplist`]: https://docs.rs/crossbeam-skiplist