use alloc::collections::VecDeque;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::cmp::Ordering;
use core::ops::Bound;
#[cfg(feature = "std")]
use parking_lot::Mutex;
#[cfg(not(feature = "std"))]
use spin::Mutex;
use super::{BlockHandle, Table};
use crate::comparator::SharedComparator;
use crate::{InternalValue, UserKey};
#[cfg(feature = "columnar")]
pub use super::index_block::RowGroupRef;
pub enum CarryUnit {
#[cfg(feature = "columnar")]
RowGroup(RowGroupRef),
DataBlock(crate::Slice),
}
pub struct CarryCandidate {
pub(crate) table: Table,
pub(crate) group: BlockHandle,
pub(crate) unit: CarryUnit,
pub(crate) rows: Arc<Vec<InternalValue>>,
keys: (UserKey, UserKey),
bytes: u64,
outcome: Outcome,
}
#[derive(Default)]
pub struct CarryOdds {
misses: core::sync::atomic::AtomicU32,
skip: core::sync::atomic::AtomicU32,
}
impl CarryOdds {
const PATIENCE: u32 = 4;
const LONGEST_SKIP: u32 = 64;
fn settle(&self, carried: bool) {
use core::sync::atomic::Ordering::Relaxed;
if carried {
self.misses.store(0, Relaxed);
self.skip.store(0, Relaxed);
return;
}
let misses = self.misses.load(Relaxed) + 1;
self.misses.store(misses, Relaxed);
if let Some(past) = misses.checked_sub(Self::PATIENCE) {
let skip = 1u32.checked_shl(past).unwrap_or(u32::MAX);
self.skip.store(skip.min(Self::LONGEST_SKIP), Relaxed);
}
}
fn skips(&self) -> bool {
use core::sync::atomic::Ordering::Relaxed;
let skip = self.skip.load(Relaxed);
if skip == 0 {
return false;
}
self.skip.store(skip - 1, Relaxed);
true
}
}
struct Outcome {
odds: Arc<CarryOdds>,
carried: bool,
}
impl Drop for Outcome {
fn drop(&mut self) {
self.odds.settle(self.carried);
}
}
pub fn row_bytes(row: &InternalValue) -> u64 {
(row.key.user_key.len() + row.value.len()) as u64
}
impl CarryCandidate {
pub(crate) fn bytes(&self) -> u64 {
self.bytes
}
pub(crate) fn first_key(&self) -> &UserKey {
&self.keys.0
}
pub(crate) fn last_key(&self) -> &UserKey {
&self.keys.1
}
pub(crate) fn is_block(&self) -> bool {
matches!(self.unit, CarryUnit::DataBlock(_))
}
pub(crate) fn mark_carried(&mut self) {
self.outcome.carried = true;
}
}
pub type CarryQueue = Arc<Candidates>;
#[derive(Default)]
pub struct Candidates {
queue: Mutex<VecDeque<CarryCandidate>>,
recorded: core::sync::atomic::AtomicUsize,
}
impl Candidates {
pub(crate) fn lock(&self) -> impl core::ops::DerefMut<Target = VecDeque<CarryCandidate>> + '_ {
self.queue.lock()
}
pub(crate) fn recorded(&self) -> usize {
self.recorded.load(core::sync::atomic::Ordering::Acquire)
}
fn record(&self, queue: &mut VecDeque<CarryCandidate>, candidate: CarryCandidate) {
queue.push_back(candidate);
self.recorded
.fetch_add(1, core::sync::atomic::Ordering::Release);
}
}
#[derive(Clone, Copy)]
pub enum CarryShape {
#[cfg(feature = "columnar")]
Groups(crate::table::meta::GroupShape),
Blocks {
restart_interval: u8,
hash_ratio_bits: u32,
},
}
#[derive(Clone)]
pub struct CarryTarget {
pub(crate) queue: CarryQueue,
pub(crate) codec: crate::CompressionType,
pub(crate) shape: CarryShape,
}
impl CarryTarget {
pub(crate) fn takes(&self, table: &Table) -> bool {
if table.metadata.data_block_compression != self.codec {
return false;
}
match self.shape {
#[cfg(feature = "columnar")]
CarryShape::Groups(shape) => {
table.carries_row_groups() && table.metadata.group_shape == Some(shape)
}
CarryShape::Blocks {
restart_interval,
hash_ratio_bits,
} => {
table.carries_data_blocks()
&& table.metadata.data_block_restart_interval == restart_interval
&& table.metadata.data_block_hash_ratio_bits == hash_ratio_bits
}
}
}
pub(crate) fn carry_for(&self, table: &Table) -> GroupCarry {
GroupCarry::new(table.clone(), Arc::clone(&self.queue))
}
}
pub struct KeyBounds {
pub(crate) lo: Bound<UserKey>,
pub(crate) hi: Bound<UserKey>,
pub(crate) comparator: SharedComparator,
}
impl KeyBounds {
fn contains(&self, key: &[u8]) -> bool {
let above_lo = match &self.lo {
Bound::Included(lo) => self.comparator.compare(key, lo) != Ordering::Less,
Bound::Excluded(lo) => self.comparator.compare(key, lo) == Ordering::Greater,
Bound::Unbounded => true,
};
above_lo
&& match &self.hi {
Bound::Included(hi) => self.comparator.compare(key, hi) != Ordering::Greater,
Bound::Excluded(hi) => self.comparator.compare(key, hi) == Ordering::Less,
Bound::Unbounded => true,
}
}
}
pub struct GroupCarry {
table: Table,
queue: CarryQueue,
bounds: Option<KeyBounds>,
rows: Arc<Vec<InternalValue>>,
spare: Arc<Vec<InternalValue>>,
next: usize,
odds: Arc<CarryOdds>,
}
impl GroupCarry {
pub(crate) fn new(table: Table, queue: CarryQueue) -> Self {
Self {
table,
queue,
bounds: None,
rows: Arc::new(Vec::new()),
spare: Arc::new(Vec::new()),
next: 0,
odds: Arc::default(),
}
}
pub(crate) fn skips_next(&self) -> bool {
self.odds.skips()
}
pub(crate) fn filters_passed_rows(&self) -> bool {
self.bounds.is_some()
}
pub(crate) fn hands_out(&self, row: &InternalValue) -> bool {
self.bounds
.as_ref()
.is_none_or(|bounds| bounds.contains(&row.key.user_key))
}
pub(crate) fn bounded(table: Table, queue: CarryQueue, bounds: KeyBounds) -> Self {
Self {
bounds: Some(bounds),
..Self::new(table, queue)
}
}
pub(crate) fn start(
&mut self,
group: Option<BlockHandle>,
frame: Option<crate::Slice>,
rows: impl IntoIterator<Item = InternalValue>,
) {
let mut queue = self.queue.lock();
if let Some(passed) = self.rows.last() {
let comparator = &self.table.comparator;
queue.retain(|candidate| {
comparator.compare(candidate.last_key(), &passed.key.user_key) == Ordering::Greater
});
}
let mut buffer = Arc::get_mut(&mut self.spare)
.map(core::mem::take)
.unwrap_or_default();
buffer.clear();
buffer.extend(rows);
let read = buffer.len();
if let Some(bounds) = &self.bounds {
buffer.retain(|row| bounds.contains(&row.key.user_key));
}
let whole = buffer.len() == read;
match Arc::get_mut(&mut self.spare) {
Some(slot) => *slot = buffer,
None => self.spare = Arc::new(buffer),
}
core::mem::swap(&mut self.rows, &mut self.spare);
self.next = 0;
let unit = match (group, frame) {
(Some(group), Some(frame)) => (!self.table.has_inner_block_layout(group.offset().0))
.then_some(CarryUnit::DataBlock(frame)),
#[cfg(feature = "columnar")]
(Some(group), None) => group.row_group().map(CarryUnit::RowGroup),
_ => None,
};
if whole
&& let (Some(group), Some(unit)) = (group, unit)
&& let (Some(first), Some(last)) = (self.rows.first(), self.rows.last())
{
let keys = (first.key.user_key.clone(), last.key.user_key.clone());
let bytes = self.rows.iter().map(row_bytes).sum();
self.queue.record(
&mut queue,
CarryCandidate {
table: self.table.clone(),
group,
unit,
rows: Arc::clone(&self.rows),
keys,
bytes,
outcome: Outcome {
odds: Arc::clone(&self.odds),
carried: false,
},
},
);
}
#[cfg(all(test, feature = "columnar"))]
tests::note_queue_len(queue.len());
}
pub(crate) fn next_row(&mut self) -> Option<InternalValue> {
let row = self.rows.get(self.next)?.clone();
self.next += 1;
Some(row)
}
}
impl Table {
#[cfg(feature = "columnar")]
pub(crate) fn carries_row_groups(&self) -> bool {
self.metadata.columnar && self.carries_as_stored() && !self.has_delete_bitmap_section()
}
pub(crate) fn carries_data_blocks(&self) -> bool {
!self.metadata.columnar && self.carries_as_stored()
}
fn carries_as_stored(&self) -> bool {
self.encryption.is_none()
&& self.metadata.ecc_params.is_none()
&& !self.metadata.ecc_unrecognized
&& self.restrict_lower_bound().is_none()
&& self.global_seqno() == 0
&& self.delete_bitmap.is_empty()
}
pub(crate) fn scan_carrying(
&self,
bounds: KeyBounds,
queue: CarryQueue,
pace: Option<&super::util::Pacer>,
) -> crate::Result<Option<super::Scanner>> {
use super::block_index::BlockIndexIter;
let min = self.metadata.key_range.min();
let past = match &bounds.hi {
Bound::Included(hi) => bounds.comparator.compare(min, hi) == Ordering::Greater,
Bound::Excluded(hi) => bounds.comparator.compare(min, hi) != Ordering::Less,
Bound::Unbounded => false,
};
if past {
return Ok(None);
}
let mut groups = Vec::new();
let walk = self.maintenance_index_walk();
let mut walk = match pace {
Some(pace) => walk.with_pace(Arc::clone(pace)),
None => walk,
};
if let Bound::Included(lo) | Bound::Excluded(lo) = &bounds.lo
&& !walk.seek_lower(lo, crate::SeqNo::MAX)
{
return Ok(None);
}
for keyed in walk {
let keyed = keyed?;
let end = keyed.end_key();
let below = match &bounds.lo {
Bound::Included(lo) => bounds.comparator.compare(end, lo) == Ordering::Less,
Bound::Excluded(lo) => bounds.comparator.compare(end, lo) != Ordering::Greater,
Bound::Unbounded => false,
};
if below {
continue;
}
groups.push(*keyed.as_ref());
let reaches_hi = match &bounds.hi {
Bound::Included(hi) => bounds.comparator.compare(end, hi) == Ordering::Greater,
Bound::Excluded(hi) => bounds.comparator.compare(end, hi) != Ordering::Less,
Bound::Unbounded => false,
};
if reaches_hi {
break;
}
}
let Some(first) = groups.first() else {
return Ok(None);
};
let start = first.offset().0;
let count = groups.len();
Ok(Some(
self.scan_groups(count, start, groups, pace)?
.with_carry(GroupCarry::bounded(self.clone(), queue, bounds)),
))
}
#[cfg(feature = "columnar")]
pub(crate) fn read_row_group_raw(
&self,
group: &BlockHandle,
pace: Option<&dyn super::util::ReadPacer>,
) -> crate::Result<crate::Slice> {
#[cfg(test)]
tests::note_raw_read(group.size());
let fd = self
.file_accessor
.peek_or_open_table(&self.global_id(), &self.path)?;
Ok(crate::file::read_exact_paced(
fd.as_ref(),
group.offset().0,
group.size() as usize,
pace,
)?)
}
}
#[cfg(all(test, feature = "columnar"))]
mod tests;