use std::{collections::BTreeMap, ops::Bound};
use vsdb_core::basic::mapx_raw::MapxRaw;
use super::error::Result;
#[derive(Default)]
pub(crate) struct StagedRows {
rows: BTreeMap<Vec<u8>, Option<Vec<u8>>>,
wiped: bool,
}
impl StagedRows {
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn put(&mut self, key: Vec<u8>, value: Vec<u8>) {
self.rows.insert(key, Some(value));
}
pub(crate) fn del(&mut self, key: Vec<u8>) {
self.rows.insert(key, None);
}
pub(crate) fn wipe(&mut self) {
self.rows.clear();
self.wiped = true;
}
pub(crate) fn get_over(&self, store: &MapxRaw, key: &[u8]) -> Option<Vec<u8>> {
match self.rows.get(key) {
Some(Some(v)) => Some(v.clone()),
Some(None) => None,
None if self.wiped => None,
None => store.get(key),
}
}
pub(crate) fn scan_prefix<'a>(
&'a self,
store: &'a MapxRaw,
prefix: &[u8],
) -> impl Iterator<Item = (Vec<u8>, Vec<u8>)> + 'a {
let lo = prefix.to_vec();
let hi = prefix_successor(prefix);
let disk_hi = match &hi {
Some(h) => Bound::Excluded(std::borrow::Cow::Owned(h.clone())),
None => Bound::Unbounded,
};
let disk = store
.range((
Bound::Included(std::borrow::Cow::Owned(lo.clone())),
disk_hi,
))
.take(if self.wiped { 0 } else { usize::MAX });
let over_hi = match &hi {
Some(h) => Bound::Excluded(h.clone()),
None => Bound::Unbounded,
};
let over = self
.rows
.range((Bound::Included(lo), over_hi))
.map(|(k, v)| (k.clone(), v.clone()));
MergeScan {
disk: disk.peekable(),
over: over.peekable(),
}
}
pub(crate) fn commit(self, store: &mut MapxRaw) -> Result<()> {
if self.rows.is_empty() && !self.wiped {
return Ok(());
}
let mut batch = if self.wiped {
store.batch_entry_wiped()
} else {
store.batch_entry()
};
for (k, v) in &self.rows {
match v {
Some(v) => batch.insert(k, v),
None => batch.remove(k),
}
}
batch.commit()
}
}
pub(crate) fn prefix_successor(prefix: &[u8]) -> Option<Vec<u8>> {
let mut s = prefix.to_vec();
for i in (0..s.len()).rev() {
if s[i] < u8::MAX {
s[i] += 1;
s.truncate(i + 1);
return Some(s);
}
}
None
}
struct MergeScan<D, O>
where
D: Iterator<Item = (Vec<u8>, Vec<u8>)>,
O: Iterator<Item = (Vec<u8>, Option<Vec<u8>>)>,
{
disk: std::iter::Peekable<D>,
over: std::iter::Peekable<O>,
}
impl<D, O> Iterator for MergeScan<D, O>
where
D: Iterator<Item = (Vec<u8>, Vec<u8>)>,
O: Iterator<Item = (Vec<u8>, Option<Vec<u8>>)>,
{
type Item = (Vec<u8>, Vec<u8>);
fn next(&mut self) -> Option<Self::Item> {
loop {
let take_over = match (self.disk.peek(), self.over.peek()) {
(None, None) => return None,
(Some(_), None) => false,
(None, Some(_)) => true,
(Some((dk, _)), Some((ok, _))) => ok <= dk,
};
if take_over {
let (ok, ov) = self.over.next().expect("peeked");
if self.disk.peek().is_some_and(|(dk, _)| *dk == ok) {
self.disk.next();
}
match ov {
Some(v) => return Some((ok, v)),
None => continue, }
} else {
return self.disk.next();
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn merge_scan_overlay_semantics() {
let mut store = MapxRaw::new();
store.insert([1u8, 1], [10u8]);
store.insert([1u8, 2], [20u8]);
store.insert([1u8, 4], [40u8]);
store.insert([2u8, 0], [99u8]);
let mut staged = StagedRows::new();
staged.del(vec![1, 2]); staged.put(vec![1, 3], vec![30]); staged.put(vec![1, 4], vec![41]);
assert_eq!(staged.get_over(&store, &[1, 1]), Some(vec![10]));
assert_eq!(staged.get_over(&store, &[1, 2]), None);
assert_eq!(staged.get_over(&store, &[1, 3]), Some(vec![30]));
assert_eq!(staged.get_over(&store, &[1, 4]), Some(vec![41]));
let scanned: Vec<_> = staged.scan_prefix(&store, &[1]).collect();
assert_eq!(
scanned,
vec![
(vec![1, 1], vec![10]),
(vec![1, 3], vec![30]),
(vec![1, 4], vec![41]),
]
);
staged.commit(&mut store).unwrap();
assert_eq!(store.get([1u8, 2]), None);
assert_eq!(store.get([1u8, 3]), Some(vec![30]));
assert_eq!(store.get([1u8, 4]), Some(vec![41]));
}
#[test]
fn wipe_semantics() {
let mut store = MapxRaw::new();
store.insert([1u8, 1], [10u8]);
store.insert([1u8, 2], [20u8]);
store.insert([2u8, 0], [99u8]);
let mut staged = StagedRows::new();
staged.put(vec![1, 9], vec![90]); staged.wipe();
staged.put(vec![1, 3], vec![30]);
assert_eq!(staged.get_over(&store, &[1, 1]), None);
assert_eq!(staged.get_over(&store, &[1, 9]), None);
assert_eq!(staged.get_over(&store, &[1, 3]), Some(vec![30]));
let scanned: Vec<_> = staged.scan_prefix(&store, &[1]).collect();
assert_eq!(scanned, vec![(vec![1, 3], vec![30])]);
staged.commit(&mut store).unwrap();
assert_eq!(store.get([1u8, 1]), None);
assert_eq!(store.get([1u8, 2]), None);
assert_eq!(store.get([2u8, 0]), None);
assert_eq!(store.get([1u8, 9]), None);
assert_eq!(store.get([1u8, 3]), Some(vec![30]));
assert_eq!(store.iter().count(), 1);
let mut staged = StagedRows::new();
staged.wipe();
staged.commit(&mut store).unwrap();
assert_eq!(store.iter().count(), 0);
}
}