Crates.io | skiplist-rust |
lib.rs | skiplist-rust |
version | 0.3.0 |
source | src |
created_at | 2024-09-25 15:17:39.003783 |
updated_at | 2024-09-29 12:53:39.311663 |
description | A lockless skiplist implementation in Rust |
homepage | |
repository | |
max_upload_size | |
id | 1386453 |
size | 35,462 |
A Rust implementation of the SkipList data structure, inspired by LevelDB's SkipList. This project provides a SkipList implementation with lock-free reads and locked writes, suitable for efficient key-value storage and retrieval.
The SkipList uses a shared Arena
for memory allocation. This means:
Add this to your Cargo.toml
:
[dependencies]
skiplist-rust = "0.3.0"
Then you can use the SkipList in your Rust code:
use skiplist_rust::{SkipList, SkipListIterator};
use skiplist_rust::arena::Arena;
use std::sync::Arc;
fn main() {
let arena = Arena::new();
let skiplist = Arc::new(SkipList::new(arena));
let mut write_handles = vec![];
for i in 0..5 {
let skiplist_clone = Arc::clone(&skiplist);
let handle = thread::spawn(move || {
let start = i * 100;
let end = start + 100;
for k in start..end {
skiplist_clone.insert(k);
println!("Thread {} inserted: {}", i, k);
}
});
write_handles.push(handle);
}
let mut read_handles = vec![];
for i in 0..3 {
let skiplist_clone = Arc::clone(&skiplist);
let handle = thread::spawn(move || {
let mut rng = rand::thread_rng();
let start = i * 100;
let end = start + 100;
for _ in start..end {
let key = rng.gen_range(0..1000);
let contains = skiplist_clone.contains(&key);
println!("Thread {} queried: {}, result: {}", i, key, contains);
thread::sleep(Duration::from_millis(1));
}
});
read_handles.push(handle);
}
for handle in write_handles {
handle.join().unwrap();
}
for handle in read_handles {
handle.join().unwrap();
}
let mut iter = skiplist.iter();
iter.seek_to_first();
println!("Final SkipList contents:");
while iter.valid() {
println!("{:?}", iter.key());
iter.next();
}
}
Arena
new() -> Arena
: Create a new Arenaallocate(bytes: usize) -> *mut u8
: Allocate memory of the specified sizeallocate_aligned(bytes: usize) -> *mut u8
: Allocate memory of the specified size with alignmentmemory_usage(&self) -> usize
: Get the current memory usage of the arenaSkipList<K>
new(arena: Arena) -> SkipList<K>
: Create a new SkipList
Creates and returns a new SkipList
instance using the provided memory arena.
insert(key: K)
: Insert a key into the SkipList (requires locking)
Inserts the given key into the SkipList. This operation acquires a write lock to ensure thread-safe modification.
contains(&key: &K) -> bool
: Check if a key exists in the SkipList (lock-free)
Checks whether the given key exists in the SkipList. This is a lock-free operation that allows concurrent reads.
iter(&self) -> SkipListIterator<K>
: Get an iterator over the SkipList (lock-free)
Returns an iterator that can be used to traverse the elements in the SkipList. This operation is lock-free, allowing concurrent iteration with other operations.
SkipListIterator<K>
new(list: &SkipList<K>) -> SkipListIterator<K>
: Create a new iterator over a SkipListvalid(&self) -> bool
: Check if the iterator is pointing to a valid nodekey(&self) -> &K
: Get the key of the current nodenext(&mut self)
: Move to the next nodeprev(&mut self)
: Move to the previous nodeseek(&mut self, target: &K)
: Seek to the first node with a key >= targetseek_to_first(&mut self)
: Seek to the first nodeseek_to_last(&mut self)
: Seek to the last nodeThis implementation aims to provide similar performance characteristics to LevelDB's SkipList. It uses atomic operations for concurrent read access and locking for write operations, providing a balance between concurrency and data consistency.
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License.