use crate::{comparator::UserComparator, range_tombstone::RangeTombstone};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::cmp::Ordering;
const ENTRY_OVERHEAD: u64 = 2 + 2 + 8;
pub(super) struct TombstoneShare {
open: Vec<usize>,
next_start: usize,
fixed: u64,
pieces: u64,
longest: u64,
carried: Vec<usize>,
zone_first: usize,
}
impl TombstoneShare {
pub(super) fn new() -> Self {
Self {
open: Vec::new(),
next_start: 0,
fixed: 0,
pieces: 0,
longest: 0,
carried: Vec::new(),
zone_first: 0,
}
}
pub(super) fn advance(
&mut self,
tombstones: &[RangeTombstone],
key: &[u8],
comparator: &dyn UserComparator,
) {
self.open_while(tombstones, key, comparator, Ordering::Less);
while let Some(tombstone) = self.open.first().and_then(|&i| tombstones.get(i))
&& comparator.compare(&tombstone.end, key) != Ordering::Greater
{
self.fixed += tombstone.end.len() as u64;
self.longest = self.longest.max(tombstone.end.len() as u64);
pop(&mut self.open, tombstones, comparator);
}
}
pub(super) fn open_through(
&mut self,
tombstones: &[RangeTombstone],
key: &[u8],
comparator: &dyn UserComparator,
) {
self.open_while(tombstones, key, comparator, Ordering::Equal);
}
fn open_while(
&mut self,
tombstones: &[RangeTombstone],
key: &[u8],
comparator: &dyn UserComparator,
ordering: Ordering,
) {
while let Some(tombstone) = tombstones.get(self.next_start)
&& comparator.compare(&tombstone.start, key) <= ordering
{
self.fixed += ENTRY_OVERHEAD + tombstone.start.len() as u64;
self.pieces += 1;
self.longest = self.longest.max(tombstone.start.len() as u64);
push(&mut self.open, self.next_start, tombstones, comparator);
self.next_start += 1;
}
}
pub(super) fn open_output(&mut self, lower: &[u8]) {
self.fixed = self.open.len() as u64 * (ENTRY_OVERHEAD + lower.len() as u64);
self.pieces = self.open.len() as u64;
self.longest = lower.len() as u64;
self.carried.clone_from(&self.open);
self.carried.sort_unstable();
self.zone_first = self.next_start;
}
pub(super) fn zone<'t>(
&'t self,
tombstones: &'t [RangeTombstone],
last: bool,
) -> impl Iterator<Item = &'t RangeTombstone> {
let end = if last {
tombstones.len()
} else {
self.next_start
};
self.carried
.iter()
.filter_map(|&i| tombstones.get(i))
.chain(tombstones.get(self.zone_first..end).unwrap_or_default())
}
pub(super) fn bytes(&self, key: &[u8]) -> u64 {
self.fixed + self.open.len() as u64 * key.len() as u64
}
pub(super) fn pieces(&self) -> u64 {
self.pieces
}
pub(super) fn longest_bound(&self, key: &[u8]) -> u64 {
self.longest.max(key.len() as u64)
}
pub(super) fn open_count(&self) -> u64 {
self.open.len() as u64
}
pub(super) fn carry(&self, key: &[u8]) -> u64 {
self.open.len() as u64 * (ENTRY_OVERHEAD + 2 * key.len() as u64)
}
pub(super) fn group(
&self,
tombstones: &[RangeTombstone],
key: &[u8],
comparator: &dyn UserComparator,
) -> Group {
tombstones
.get(self.next_start..)
.unwrap_or_default()
.iter()
.take_while(|rt| comparator.compare(&rt.start, key) == Ordering::Equal)
.fold(Group::default(), |group, rt| Group {
bytes: group.bytes + ENTRY_OVERHEAD + (rt.start.len() + rt.end.len()) as u64,
entries: group.entries + 1,
longest: group.longest.max(rt.end.len() as u64),
})
}
#[cfg(feature = "columnar")]
pub(super) fn starts_within(
&self,
tombstones: &[RangeTombstone],
(after, through): (&[u8], &[u8]),
comparator: &dyn UserComparator,
) -> bool {
tombstones
.get(self.next_start..)
.unwrap_or_default()
.iter()
.find(|rt| comparator.compare(&rt.start, after) == Ordering::Greater)
.is_some_and(|rt| comparator.compare(&rt.start, through) != Ordering::Greater)
}
pub(super) fn first_open_end<'t>(
&self,
tombstones: &'t [RangeTombstone],
) -> Option<&'t crate::UserKey> {
self.open
.first()
.and_then(|&i| tombstones.get(i))
.map(|rt| &rt.end)
}
pub(super) fn has_more(&self, tombstones: &[RangeTombstone]) -> bool {
!self.open.is_empty() || self.next_start < tombstones.len()
}
pub(super) fn next_pending(&self) -> usize {
self.next_start
}
#[cfg(test)]
fn tracked(&self) -> usize {
self.open.capacity()
}
}
#[derive(Clone, Copy, Default)]
pub(super) struct Group {
pub(super) bytes: u64,
pub(super) entries: u64,
pub(super) longest: u64,
}
fn ends_before(
tombstones: &[RangeTombstone],
a: usize,
b: usize,
comparator: &dyn UserComparator,
) -> bool {
tombstones
.get(a)
.zip(tombstones.get(b))
.is_some_and(|(a, b)| comparator.compare(&a.end, &b.end) == Ordering::Less)
}
fn push(
heap: &mut Vec<usize>,
index: usize,
tombstones: &[RangeTombstone],
comparator: &dyn UserComparator,
) {
heap.push(index);
let mut at = heap.len() - 1;
while at > 0 {
let parent = (at - 1) / 2;
match (heap.get(at), heap.get(parent)) {
(Some(&child), Some(&up)) if ends_before(tombstones, child, up, comparator) => {
heap.swap(at, parent);
at = parent;
}
_ => break,
}
}
}
fn pop(heap: &mut Vec<usize>, tombstones: &[RangeTombstone], comparator: &dyn UserComparator) {
let (Some(last), Some(root)) = (heap.pop(), heap.first_mut()) else {
return;
};
*root = last;
let mut at = 0;
loop {
let mut least = at;
for child in [2 * at + 1, 2 * at + 2] {
if let (Some(&c), Some(&l)) = (heap.get(child), heap.get(least))
&& ends_before(tombstones, c, l, comparator)
{
least = child;
}
}
if least == at {
break;
}
heap.swap(at, least);
at = least;
}
}
#[cfg(test)]
mod tests;