use std::collections::{BTreeMap, btree_map};
use crate::record::Record;
#[derive(Debug, Default)]
pub(crate) struct MemTable {
entries: BTreeMap<Vec<u8>, Record>,
approx_size: usize,
}
impl MemTable {
#[inline]
pub(crate) fn new() -> Self {
MemTable {
entries: BTreeMap::new(),
approx_size: 0,
}
}
pub(crate) fn apply(&mut self, key: Vec<u8>, record: Record) {
self.insert(key, record);
}
fn insert(&mut self, key: Vec<u8>, record: Record) {
let added = key.len() + record.value_len();
match self.entries.entry(key) {
btree_map::Entry::Occupied(mut slot) => {
self.approx_size = self.approx_size - slot.get().value_len() + record.value_len();
let _ = slot.insert(record);
}
btree_map::Entry::Vacant(slot) => {
self.approx_size += added;
let _ = slot.insert(record);
}
}
}
#[inline]
pub(crate) fn get(&self, key: &[u8]) -> Option<&Record> {
self.entries.get(key)
}
#[inline]
pub(crate) fn approx_size(&self) -> usize {
self.approx_size
}
#[inline]
pub(crate) fn is_empty(&self) -> bool {
self.entries.is_empty()
}
#[inline]
pub(crate) fn iter(&self) -> btree_map::Iter<'_, Vec<u8>, Record> {
self.entries.iter()
}
pub(crate) fn take(&mut self) -> BTreeMap<Vec<u8>, Record> {
self.approx_size = 0;
std::mem::take(&mut self.entries)
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
impl MemTable {
fn put(&mut self, key: Vec<u8>, value: Vec<u8>) {
self.apply(key, Record::Value(value));
}
fn delete(&mut self, key: Vec<u8>) {
self.apply(key, Record::Tombstone);
}
}
#[test]
fn test_put_then_get_returns_value() {
let mut m = MemTable::new();
m.put(b"a".to_vec(), b"1".to_vec());
assert_eq!(m.get(b"a"), Some(&Record::Value(b"1".to_vec())));
}
#[test]
fn test_delete_records_tombstone() {
let mut m = MemTable::new();
m.put(b"a".to_vec(), b"1".to_vec());
m.delete(b"a".to_vec());
assert_eq!(m.get(b"a"), Some(&Record::Tombstone));
}
#[test]
fn test_missing_key_returns_none() {
let m = MemTable::new();
assert_eq!(m.get(b"absent"), None);
}
#[test]
fn test_approx_size_tracks_inserts() {
let mut m = MemTable::new();
assert_eq!(m.approx_size(), 0);
m.put(b"ab".to_vec(), b"xyz".to_vec()); assert_eq!(m.approx_size(), 5);
m.put(b"k".to_vec(), b"v".to_vec()); assert_eq!(m.approx_size(), 7);
}
#[test]
fn test_approx_size_replaces_value_not_key() {
let mut m = MemTable::new();
m.put(b"k".to_vec(), b"short".to_vec()); assert_eq!(m.approx_size(), 6);
m.put(b"k".to_vec(), b"longer!".to_vec()); assert_eq!(m.approx_size(), 8);
m.delete(b"k".to_vec()); assert_eq!(m.approx_size(), 1);
}
#[test]
fn test_iter_is_sorted() {
let mut m = MemTable::new();
m.put(b"c".to_vec(), b"3".to_vec());
m.put(b"a".to_vec(), b"1".to_vec());
m.put(b"b".to_vec(), b"2".to_vec());
let keys: Vec<&[u8]> = m.iter().map(|(k, _)| k.as_slice()).collect();
assert_eq!(keys, vec![b"a".as_slice(), b"b", b"c"]);
}
#[test]
fn test_take_empties_and_resets_size() {
let mut m = MemTable::new();
m.put(b"a".to_vec(), b"1".to_vec());
let taken = m.take();
assert_eq!(taken.len(), 1);
assert!(m.is_empty());
assert_eq!(m.approx_size(), 0);
}
}