use core::ops::Bound;
use alloc::sync::Arc;
use alloc::vec::Vec;
use crate::key::InternalKey;
use crate::memtable::Memtable;
use crate::table::columnar::{ColumnBatch, entries_to_column_batch};
use crate::table::columnar_cursor::ColumnarCursor;
use crate::table::columnar_predicate::PredicateSupport;
use crate::{InternalValue, SeqNo, UserKey};
const ROWS_PER_BATCH: usize = 1_024;
const BATCH_BASE_BYTES: u64 = 8;
const ROW_OVERHEAD_BYTES: u64 = 4 + 4 + 8 + 1;
fn row_bytes(row: &InternalValue) -> u64 {
(row.key.user_key.len() + row.value.len()) as u64 + ROW_OVERHEAD_BYTES
}
struct Gather {
entries: Vec<InternalValue>,
bytes: u64,
share: u64,
}
impl Gather {
fn fits(&self, row: &InternalValue) -> bool {
self.entries.is_empty() || self.bytes + row_bytes(row) <= self.share
}
fn push(&mut self, row: InternalValue) {
self.bytes += row_bytes(&row);
self.entries.push(row);
}
fn open(&self) -> bool {
self.entries.len() < ROWS_PER_BATCH && self.bytes < self.share
}
}
type TableRows = alloc::boxed::Box<dyn Iterator<Item = crate::Result<InternalValue>> + Send>;
enum RowInput {
Table {
table: crate::Table,
hi: Bound<UserKey>,
rows: TableRows,
resume: Option<(UserKey, SeqNo)>,
},
Memtable {
memtable: Arc<Memtable>,
lo: Bound<InternalKey>,
hi: Bound<InternalKey>,
},
}
pub(super) struct RowCursor {
input: RowInput,
snapshot: SeqNo,
columns: Vec<u16>,
share: u64,
oversized: u64,
}
impl RowCursor {
pub(super) fn table(
table: &crate::Table,
lo: Bound<UserKey>,
hi: Bound<UserKey>,
snapshot: SeqNo,
columns: Vec<u16>,
share: u64,
) -> Self {
Self {
input: RowInput::Table {
rows: alloc::boxed::Box::new(table.range((lo, hi.clone()))),
table: table.clone(),
hi,
resume: None,
},
snapshot,
columns,
share,
oversized: 0,
}
}
pub(super) fn memtable(
memtable: Arc<Memtable>,
lo: &Bound<UserKey>,
hi: &Bound<UserKey>,
snapshot: SeqNo,
columns: Vec<u16>,
share: u64,
) -> Self {
let (lo, hi) = crate::range::internal_bounds(lo, hi);
Self {
input: RowInput::Memtable { memtable, lo, hi },
snapshot,
columns,
share,
oversized: 0,
}
}
fn next_batch(&mut self) -> Option<crate::Result<ColumnBatch>> {
let snapshot = self.snapshot;
let mut gather = Gather {
entries: Vec::new(),
bytes: BATCH_BASE_BYTES,
share: self.share,
};
match &mut self.input {
RowInput::Table {
table,
hi,
rows,
resume,
} => {
let mut skip = resume.take();
if let Some((key, _)) = &skip {
*rows = alloc::boxed::Box::new(
table.range((Bound::Included(key.clone()), hi.clone())),
);
}
while gather.open() {
match rows.next() {
None => break,
Some(Err(e)) => return Some(Err(e)),
Some(Ok(row))
if skip.as_ref().is_some_and(|(key, seqno)| {
row.key.user_key == *key && row.key.seqno > *seqno
}) => {}
Some(Ok(row)) if row.key.seqno < snapshot => {
skip = None;
if !gather.fits(&row) {
*resume = Some((row.key.user_key.clone(), row.key.seqno));
break;
}
gather.push(row);
}
Some(Ok(_)) => {}
}
}
}
RowInput::Memtable { memtable, lo, hi } => {
for row in memtable
.range_internal((lo.clone(), hi.clone()))
.filter(|row| row.key.seqno < snapshot)
{
if !gather.fits(&row) {
break;
}
gather.push(row);
if !gather.open() {
break;
}
}
if let Some(last) = gather.entries.last() {
*lo = Bound::Excluded(last.key.clone());
}
}
}
if gather.entries.is_empty() {
return None;
}
if gather.bytes > gather.share {
self.oversized += 1;
}
Some(self.build(&gather.entries))
}
fn take_oversized(&mut self) -> u64 {
core::mem::take(&mut self.oversized)
}
fn build(&self, entries: &[InternalValue]) -> crate::Result<ColumnBatch> {
let ColumnBatch {
row_count,
mut columns,
} = entries_to_column_batch(entries)?;
let mut out = Vec::with_capacity(self.columns.len());
for id in &self.columns {
if let Some(at) = columns.iter().position(|c| c.column_id == *id) {
out.push(columns.swap_remove(at));
}
}
Ok(ColumnBatch {
row_count,
columns: out,
})
}
}
pub(super) enum SourceCursor {
Columnar(alloc::boxed::Box<ColumnarCursor>),
Rows(alloc::boxed::Box<RowCursor>),
}
impl SourceCursor {
pub(super) fn next(&mut self) -> Option<crate::Result<ColumnBatch>> {
match self {
Self::Columnar(cursor) => cursor.next(),
Self::Rows(rows) => rows.next_batch(),
}
}
pub(super) fn next_located(
&mut self,
) -> Option<crate::Result<(ColumnBatch, Option<crate::table::columnar_cursor::PageAt>)>> {
match self {
Self::Columnar(cursor) => cursor
.next_located()
.map(|located| located.map(|(batch, at)| (batch, Some(at)))),
Self::Rows(rows) => rows.next_batch().map(|batch| batch.map(|b| (b, None))),
}
}
pub(super) fn table(&self) -> Option<&crate::Table> {
match self {
Self::Columnar(cursor) => Some(cursor.table()),
Self::Rows(_) => None,
}
}
pub(super) fn set_projection(&mut self, projection: &[u16]) {
if let Self::Columnar(cursor) = self {
cursor.set_projection(projection);
}
}
pub(super) fn held_bytes(&self) -> u64 {
match self {
Self::Columnar(cursor) => cursor.held_bytes(),
Self::Rows(_) => 0,
}
}
pub(super) fn take_oversized(&mut self) -> u64 {
match self {
Self::Columnar(cursor) => cursor.take_oversized(),
Self::Rows(rows) => rows.take_oversized(),
}
}
pub(super) fn predicate_support(&self) -> PredicateSupport {
match self {
Self::Columnar(cursor) => cursor.predicate_support(),
Self::Rows(_) => PredicateSupport::Exact,
}
}
}