1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
use crate::fs_utils;
use anyhow::Result;
use pancake_types::{iters::KeyValueReader, types::Deser};
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::fs::OpenOptions;
use std::path::{Path, PathBuf};

/// A MemLog is a sorted dictionary (called Memtable), backed up by a write-ahead log file.
pub struct ReadonlyMemLog<K, V> {
    pub memtable: BTreeMap<K, V>,
    pub log_path: PathBuf,
}

impl<K, V> ReadonlyMemLog<K, V>
where
    K: Deser + Ord,
    V: Deser,
{
    pub fn load<P: AsRef<Path>>(log_path: P) -> Result<Self> {
        let log_path = log_path.as_ref();

        let mut memtable = BTreeMap::default();
        if log_path.exists() {
            let log_file = fs_utils::open_file(log_path, OpenOptions::new().read(true))?;
            let iter = KeyValueReader::<_, K, V>::from(log_file).into_iter_kv();
            for res_kv in iter {
                let (k, v) = res_kv?;
                memtable.insert(k, v);
            }
        }

        Ok(Self {
            memtable,
            log_path: log_path.into(),
        })
    }

    pub fn mem_len(&self) -> usize {
        self.memtable.len()
    }

    pub fn get_one<Q>(&self, k: &Q) -> Option<(&K, &V)>
    where
        Q: Ord,
        K: Borrow<Q>,
    {
        self.memtable.get_key_value(k)
    }

    pub fn get_range<'a, Q>(
        &'a self,
        k_lo: Option<&'a Q>,
        k_hi: Option<&'a Q>,
    ) -> impl Iterator<Item = (&K, &V)>
    where
        K: PartialOrd<Q>,
    {
        // TODO replace `.skip_while()` with https://doc.rust-lang.org/stable/std/collections/struct.BTreeMap.html#method.lower_bound when the latter graduates into the stable rust.
        self.memtable
            .iter()
            .skip_while(move |(sample_k, _v)| match k_lo {
                None => false,
                Some(k_lo) => sample_k
                    .partial_cmp(&k_lo)
                    .unwrap_or(Ordering::Greater)
                    .is_lt(),
            })
            .take_while(move |(sample_k, _v)| match k_hi {
                None => true,
                Some(k_hi) => sample_k
                    .partial_cmp(&k_hi)
                    .unwrap_or(Ordering::Less)
                    .is_le(),
            })
    }

    pub fn get_whole_range(&self) -> impl Iterator<Item = (&K, &V)> {
        self.memtable.iter()
    }
}