use bincode::serialized_size;
use lsm_tree::compaction::{CompactionIter, CompactionStrategy};
use lsm_tree::{Result, SSTable, SSTableBuilder, SSTableValue};
use serde::de::DeserializeOwned;
use serde::ser::Serialize;
use std::borrow::Borrow;
use std::cmp;
use std::collections::BTreeMap;
use std::hash::Hash;
use std::mem;
pub struct LsmMap<T, U, C> {
in_memory_tree: BTreeMap<T, SSTableValue<U>>,
in_memory_usage: u64,
compaction_strategy: C,
}
impl<T, U, C> LsmMap<T, U, C>
where
T: Clone + Ord + Hash + DeserializeOwned + Serialize,
U: Clone + DeserializeOwned + Serialize,
C: CompactionStrategy<T, U>,
{
pub fn new(compaction_strategy: C) -> Self {
LsmMap {
in_memory_tree: BTreeMap::new(),
in_memory_usage: 0,
compaction_strategy,
}
}
fn try_compact(&mut self) -> Result<()> {
self.in_memory_usage = 0;
let mut sstable_builder = SSTableBuilder::new(
self.compaction_strategy.get_path(),
self.in_memory_tree.len(),
)?;
for entry in mem::replace(&mut self.in_memory_tree, BTreeMap::new()) {
sstable_builder.append(entry.0, entry.1)?;
}
let sstable = SSTable::new(sstable_builder.flush()?)?;
self.compaction_strategy.try_compact(sstable)
}
pub fn insert(&mut self, key: T, value: U) -> Result<()> {
let value = SSTableValue {
data: Some(value),
logical_time: self.compaction_strategy.get_and_increment_logical_time()?,
};
let key_size = serialized_size(&key)?;
let value_size = serialized_size(&value)?;
if let Some(ref value) = self.in_memory_tree.get(&key) {
let value_size = serialized_size(value)?;
self.in_memory_usage -= key_size + value_size;
}
self.in_memory_usage += key_size + value_size;
self.in_memory_tree.insert(key, value);
if self.in_memory_usage > self.compaction_strategy.get_max_in_memory_size() {
self.try_compact()
} else {
Ok(())
}
}
pub fn remove(&mut self, key: T) -> Result<()> {
let key_size = serialized_size(&key)?;
let value = SSTableValue {
data: None,
logical_time: self.compaction_strategy.get_and_increment_logical_time()?,
};
if let Some(ref value) = self.in_memory_tree.get(&key) {
let value_size = serialized_size(value)?;
self.in_memory_usage -= key_size + value_size;
}
self.in_memory_usage += serialized_size(&key)?;
self.in_memory_usage += serialized_size(&value)?;
self.in_memory_tree.insert(key, value);
if self.in_memory_usage > self.compaction_strategy.get_max_in_memory_size() {
self.try_compact()
} else {
Ok(())
}
}
pub fn contains_key<V>(&mut self, key: &V) -> Result<bool>
where
T: Borrow<V>,
V: Ord + Hash + ?Sized,
{
self.get(key).map(|value| value.is_some())
}
pub fn get<V>(&mut self, key: &V) -> Result<Option<U>>
where
T: Borrow<V>,
V: Ord + Hash + ?Sized,
{
if let Some(value) = self.in_memory_tree.get(&key) {
Ok(value.data.clone())
} else {
self.compaction_strategy
.get(key)
.map(|value_opt| value_opt.and_then(|value| value.data))
}
}
pub fn len_hint(&mut self) -> Result<usize> {
Ok(self.in_memory_tree.len() + self.compaction_strategy.len_hint()?)
}
pub fn len(&mut self) -> Result<usize> {
self.flush()?;
self.compaction_strategy.len()
}
pub fn is_empty(&mut self) -> Result<bool> {
self.len().map(|len| len == 0)
}
pub fn clear(&mut self) -> Result<()> {
self.in_memory_tree.clear();
self.compaction_strategy.clear()
}
pub fn min(&mut self) -> Result<Option<T>> {
let in_memory_min = self.in_memory_tree
.iter()
.skip_while(|entry| entry.1.data.is_none())
.next()
.map(|entry| entry.0.clone());
let disk_min = self.compaction_strategy.min()?;
if in_memory_min.is_none() {
Ok(disk_min)
} else if disk_min.is_none() {
Ok(in_memory_min)
} else {
Ok(cmp::min(in_memory_min, disk_min))
}
}
pub fn max(&mut self) -> Result<Option<T>> {
Ok(cmp::max(
self.in_memory_tree
.iter()
.rev()
.skip_while(|entry| entry.1.data.is_none())
.next()
.map(|entry| entry.0.clone()),
self.compaction_strategy.max()?,
))
}
pub fn flush(&mut self) -> Result<()> {
if !self.in_memory_tree.is_empty() {
self.try_compact()?;
}
self.compaction_strategy.flush()
}
pub fn iter(&mut self) -> Result<Box<CompactionIter<T, U>>> {
self.flush()?;
self.compaction_strategy.iter()
}
}