use crate::errors::NitriteResult;
use crossbeam_skiplist::SkipMap;
use std::collections::BTreeMap;
use std::collections::Bound::Included;
use std::ops::Bound::{Excluded, Unbounded};
pub trait NavigableMap<K, V> {
fn first_key(&self) -> NitriteResult<Option<K>>;
fn last_key(&self) -> NitriteResult<Option<K>>;
fn higher_key(&self, key: &K) -> NitriteResult<Option<K>>;
fn ceiling_key(&self, key: &K) -> NitriteResult<Option<K>>;
fn lower_key(&self, key: &K) -> NitriteResult<Option<K>>;
fn floor_key(&self, key: &K) -> NitriteResult<Option<K>>;
}
impl<K, V> NavigableMap<K, V> for BTreeMap<K, V>
where
K: Ord + Clone,
{
#[inline]
fn first_key(&self) -> NitriteResult<Option<K>> {
Ok(self.keys().next().cloned())
}
#[inline]
fn last_key(&self) -> NitriteResult<Option<K>> {
Ok(self.keys().next_back().cloned())
}
#[inline]
fn higher_key(&self, key: &K) -> NitriteResult<Option<K>> {
Ok(self
.range((Excluded(key), Unbounded))
.next()
.map(|(k, _)| k.clone()))
}
#[inline]
fn ceiling_key(&self, key: &K) -> NitriteResult<Option<K>> {
Ok(self
.range((Included(key), Unbounded))
.next()
.map(|(k, _)| k.clone()))
}
#[inline]
fn lower_key(&self, key: &K) -> NitriteResult<Option<K>> {
Ok(self
.range((Unbounded, Excluded(key)))
.next_back()
.map(|(k, _)| k.clone()))
}
#[inline]
fn floor_key(&self, key: &K) -> NitriteResult<Option<K>> {
Ok(self
.range((Unbounded, Included(key)))
.next_back()
.map(|(k, _)| k.clone()))
}
}
impl<K, V> NavigableMap<K, V> for SkipMap<K, V>
where
K: Ord + Clone,
{
#[inline]
fn first_key(&self) -> NitriteResult<Option<K>> {
Ok(self.front().map(|entry| entry.key().clone()))
}
#[inline]
fn last_key(&self) -> NitriteResult<Option<K>> {
Ok(self.back().map(|entry| entry.key().clone()))
}
#[inline]
fn higher_key(&self, key: &K) -> NitriteResult<Option<K>> {
Ok(self
.range((Excluded(key), Unbounded))
.next()
.map(|entry| entry.key().clone()))
}
#[inline]
fn ceiling_key(&self, key: &K) -> NitriteResult<Option<K>> {
Ok(self
.range((Included(key), Unbounded))
.next()
.map(|entry| entry.key().clone()))
}
#[inline]
fn lower_key(&self, key: &K) -> NitriteResult<Option<K>> {
Ok(self
.range((Unbounded, Excluded(key)))
.next_back()
.map(|entry| entry.key().clone()))
}
#[inline]
fn floor_key(&self, key: &K) -> NitriteResult<Option<K>> {
Ok(self
.range((Unbounded, Included(key)))
.next_back()
.map(|entry| entry.key().clone()))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::common::{Key, Value};
use crossbeam_skiplist::SkipMap;
#[test]
fn test_btree_map_first_key() {
let mut map = BTreeMap::new();
map.insert(1, "a");
map.insert(2, "b");
assert_eq!(map.first_key().unwrap(), Some(1));
}
#[test]
fn test_btree_map_last_key() {
let mut map = BTreeMap::new();
map.insert(1, "a");
map.insert(2, "b");
assert_eq!(map.last_key().unwrap(), Some(2));
}
#[test]
fn test_btree_map_higher_key() {
let mut map = BTreeMap::new();
map.insert(1, "a");
map.insert(2, "b");
assert_eq!(map.higher_key(&1).unwrap(), Some(2));
}
#[test]
fn test_btree_map_ceiling_key() {
let mut map = BTreeMap::new();
map.insert(1, "a");
map.insert(2, "b");
assert_eq!(map.ceiling_key(&1).unwrap(), Some(1));
}
#[test]
fn test_btree_map_lower_key() {
let mut map = BTreeMap::new();
map.insert(1, "a");
map.insert(2, "b");
assert_eq!(map.lower_key(&2).unwrap(), Some(1));
}
#[test]
fn test_btree_map_floor_key() {
let mut map = BTreeMap::new();
map.insert(1, "a");
map.insert(2, "b");
assert_eq!(map.floor_key(&2).unwrap(), Some(2));
}
#[test]
fn test_skip_map_first_key() {
let map = SkipMap::new();
map.insert(1, "a");
map.insert(2, "b");
assert_eq!(map.first_key().unwrap(), Some(1));
}
#[test]
fn test_skip_map_last_key() {
let map = SkipMap::new();
map.insert(1, "a");
map.insert(2, "b");
assert_eq!(map.last_key().unwrap(), Some(2));
}
#[test]
fn test_skip_map_higher_key() {
let map = SkipMap::new();
map.insert(1, "a");
map.insert(2, "b");
assert_eq!(map.higher_key(&1).unwrap(), Some(2));
}
#[test]
fn test_skip_map_ceiling_key() {
let map = SkipMap::new();
map.insert(1, "a");
map.insert(2, "b");
assert_eq!(map.ceiling_key(&1).unwrap(), Some(1));
}
#[test]
fn test_skip_map_lower_key() {
let map = SkipMap::new();
map.insert(1, "a");
map.insert(2, "b");
assert_eq!(map.lower_key(&2).unwrap(), Some(1));
}
#[test]
fn test_skip_map_floor_key() {
let map = SkipMap::new();
map.insert(1, "a");
map.insert(2, "b");
assert_eq!(map.floor_key(&2).unwrap(), Some(2));
}
#[test]
fn test() {
let mut map = BTreeMap::new();
let key1 = Key::from("key1");
let key2 = Key::from("key2");
let value = Value::from("value1");
map.insert(key1.clone(), value.clone());
map.insert(key2.clone(), value.clone());
assert_eq!(map.floor_key(&key2).unwrap(), Some(key2));
}
#[test]
fn bench_btree_map_navigable_ops() {
let mut map = BTreeMap::new();
for i in 0..1000 {
map.insert(i, format!("value{}", i));
}
let start = std::time::Instant::now();
for _ in 0..1000 {
for i in 0..100 {
let _ = map.first_key();
let _ = map.last_key();
let _ = map.higher_key(&i);
let _ = map.ceiling_key(&i);
let _ = map.lower_key(&i);
let _ = map.floor_key(&i);
}
}
let elapsed = start.elapsed();
println!(
"BTreeMap navigable ops (1000x 100 queries): {:?} ({:.3}µs per op set)",
elapsed,
elapsed.as_micros() as f64 / 1000.0 / 100.0
);
}
#[test]
fn bench_skip_map_navigable_ops() {
let map = SkipMap::new();
for i in 0..1000 {
map.insert(i, format!("value{}", i));
}
let start = std::time::Instant::now();
for _ in 0..1000 {
for i in 0..100 {
let _ = map.first_key();
let _ = map.last_key();
let _ = map.higher_key(&i);
let _ = map.ceiling_key(&i);
let _ = map.lower_key(&i);
let _ = map.floor_key(&i);
}
}
let elapsed = start.elapsed();
println!(
"SkipMap navigable ops (1000x 100 queries): {:?} ({:.3}µs per op set)",
elapsed,
elapsed.as_micros() as f64 / 1000.0 / 100.0
);
}
}