use std::ops::Range;
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use vortex_error::VortexResult;
use vortex_mask::Mask;
use vortex_session::VortexSession;
use super::DEFAULT_CAPACITY;
use super::port::PortId;
use super::port::SourceEnd;
use super::scan::Core;
use super::values::ValueCache;
use crate::plan::physical::ConcatPlan;
use crate::plan::physical::PlanRef;
use crate::plan::physical::SegmentScanPlan;
use crate::plan::physical::operator::Operator;
use crate::plan::physical::operator::Source;
use crate::plan::physical::operators::ConcatSource;
use crate::plan::physical::operators::ScanSource;
use crate::plan::physical::operators::SelectStage;
use crate::segments::SegmentId;
pub struct Chain {
pub(crate) source: Box<dyn Source>,
pub(crate) stages: Vec<Box<dyn Operator>>,
pub(crate) inlets: Vec<PortId>,
}
impl Chain {
pub fn new(source: impl Source + 'static) -> Self {
Self {
source: Box::new(source),
stages: Vec::new(),
inlets: Vec::new(),
}
}
pub(crate) fn port(port: PortId) -> Self {
Self {
source: Box::new(ConcatSource::new(1)),
stages: Vec::new(),
inlets: vec![port],
}
}
pub fn with(mut self, stage: impl Operator + 'static) -> Self {
self.stages.push(Box::new(stage));
self
}
}
impl Core {
pub(crate) fn compile(
&mut self,
plan: &PlanRef,
rows: Range<u64>,
mask: &Mask,
) -> VortexResult<Option<Chain>> {
Compiler { core: self }.compile(plan, rows, mask)
}
}
pub struct Compiler<'a> {
pub(crate) core: &'a mut Core,
}
impl Compiler<'_> {
pub fn compile(
&mut self,
plan: &PlanRef,
rows: Range<u64>,
mask: &Mask,
) -> VortexResult<Option<Chain>> {
if rows.start >= rows.end {
return Ok(None);
}
plan.compile(rows, mask, self)
}
pub fn join(&mut self, chains: Vec<Chain>, source: impl Source + 'static) -> Chain {
self.join_with(chains, |_| DEFAULT_CAPACITY, source)
}
pub fn join_with(
&mut self,
chains: Vec<Chain>,
capacity: impl Fn(usize) -> usize,
source: impl Source + 'static,
) -> Chain {
let inlets = chains
.into_iter()
.enumerate()
.map(|(index, chain)| {
let port = self
.core
.arena
.create(capacity(index), None, SourceEnd::Unclaimed);
self.core.add_pipeline(chain, smallvec::smallvec![port]);
port
})
.collect();
Chain {
source: Box::new(source),
stages: Vec::new(),
inlets,
}
}
pub fn row_offset(&self) -> u64 {
self.core.row_offset
}
pub fn session(&self) -> &VortexSession {
&self.core.session
}
pub(crate) fn values(&self) -> &ValueCache {
&self.core.values
}
pub(crate) fn scan(
&mut self,
scan: &SegmentScanPlan,
rows: Range<u64>,
filter: Option<Mask>,
) -> VortexResult<Option<Chain>> {
let filter = match filter {
Some(filter) if filter.all_false() => return Ok(None),
Some(filter) if filter.all_true() => None,
filter => filter,
};
let slice = if rows.start == 0 && rows.end == scan.row_count() {
None
} else {
Some(usize::try_from(rows.start)?..usize::try_from(rows.end)?)
};
let split = self.core.split;
Ok(Some(match self.claim(scan, split) {
None => Chain::new(ScanSource::new(scan.clone(), slice, filter)),
Some(port) => {
let chain = Chain::port(port);
if slice.is_none() && filter.is_none() {
chain
} else {
chain.with(SelectStage::new(slice, filter))
}
}
}))
}
fn claim(&mut self, scan: &SegmentScanPlan, split: usize) -> Option<PortId> {
if split == usize::MAX {
return None;
}
let segment = scan.segment_id();
let shares = &mut self.core.shares;
let ports = &mut shares.ports[split];
if let Some(waiting) = ports.get_mut(&segment) {
let port = waiting.pop();
if waiting.is_empty() {
ports.remove(&segment);
}
return port;
}
let ranges = shares.shared.remove(&segment)?;
let mut claimants: SmallVec<[usize; 4]> = SmallVec::new();
for rows in &ranges {
claimants.extend(
shares
.overlapping(rows)
.filter(|&split| !shares.finished[split] && shares.overlaps(split, rows)),
);
}
if claimants.len() <= 1 {
return None;
}
let mut outlets: SmallVec<[PortId; 1]> = SmallVec::with_capacity(claimants.len());
let mut mine = None;
for claimant in claimants {
let port = self
.core
.arena
.create(usize::MAX, None, SourceEnd::Unclaimed);
outlets.push(port);
if claimant == split && mine.is_none() {
mine = Some(port);
} else {
self.core.shares.ports[claimant]
.entry(segment)
.or_default()
.push(port);
}
}
self.core.add_pipeline(
Chain::new(ScanSource::new(scan.clone(), None, None)),
outlets,
);
mine
}
}
#[derive(Default)]
pub(crate) struct Shares {
splits: Vec<Range<u64>>,
ordered: bool,
finished: Vec<bool>,
reaches: Vec<(SegmentId, Range<u64>)>,
shared: FxHashMap<SegmentId, SmallVec<[Range<u64>; 1]>>,
ports: Vec<FxHashMap<SegmentId, SmallVec<[PortId; 1]>>>,
}
impl Shares {
pub(crate) fn new(splits: Vec<Range<u64>>) -> Self {
let ordered = splits.windows(2).all(|pair| pair[0].end <= pair[1].start);
Self {
finished: vec![false; splits.len()],
ports: (0..splits.len()).map(|_| FxHashMap::default()).collect(),
splits,
ordered,
reaches: Vec::new(),
shared: FxHashMap::default(),
}
}
fn overlapping(&self, rows: &Range<u64>) -> Range<usize> {
if self.ordered {
let first = self.splits.partition_point(|split| split.end <= rows.start);
let end = self.splits.partition_point(|split| split.start < rows.end);
return first..end.max(first);
}
let mut overlapping = self
.splits
.iter()
.enumerate()
.filter(|(_, split)| split.start < rows.end && rows.start < split.end)
.map(|(index, _)| index);
match overlapping.next() {
Some(first) => first..overlapping.next_back().unwrap_or(first) + 1,
None => 0..0,
}
}
fn overlaps(&self, split: usize, rows: &Range<u64>) -> bool {
let split = &self.splits[split];
split.start < rows.end && rows.start < split.end
}
pub(crate) fn add(&mut self, plan: &PlanRef, rows: Range<u64>) -> VortexResult<()> {
let reaches = &mut self.reaches;
plan.reach(rows, &Reach::Offset(0), &mut |segment, rows| {
reaches.push((segment, rows))
})
}
pub(crate) fn retain_shared(&mut self) {
let mut reaches = std::mem::take(&mut self.reaches);
reaches.sort_unstable_by_key(|(segment, _)| *segment);
for readers in reaches.chunk_by(|(left, _), (right, _)| left == right) {
let (segment, rows) = &readers[0];
if readers.len() == 1
&& self
.overlapping(rows)
.filter(|&split| self.overlaps(split, rows))
.nth(1)
.is_none()
{
continue;
}
self.shared.insert(
*segment,
readers.iter().map(|(_, rows)| rows.clone()).collect(),
);
}
}
#[cfg(debug_assertions)]
pub(crate) fn waiting(&self) -> impl Iterator<Item = PortId> + '_ {
self.ports
.iter()
.flat_map(|ports| ports.values().flatten().copied())
}
pub(crate) fn finish(&mut self, split: usize) -> SmallVec<[PortId; 4]> {
self.finished[split] = true;
std::mem::take(&mut self.ports[split])
.into_values()
.flatten()
.collect()
}
}
#[derive(Clone, Debug)]
pub enum Reach {
Offset(u64),
Fixed(Range<u64>),
}
impl Reach {
pub fn root(&self, rows: &Range<u64>) -> Range<u64> {
match self {
Reach::Offset(offset) => rows.start + offset..rows.end + offset,
Reach::Fixed(root) => root.clone(),
}
}
pub fn shift(&self, by: u64) -> Self {
match self {
Reach::Offset(offset) => Reach::Offset(offset + by),
Reach::Fixed(root) => Reach::Fixed(root.clone()),
}
}
pub fn fixed(&self, rows: &Range<u64>) -> Self {
Reach::Fixed(self.root(rows))
}
}
pub(crate) fn overlapping<'a>(
concat: &'a ConcatPlan,
rows: &Range<u64>,
) -> impl Iterator<Item = (usize, u64, Range<u64>)> + 'a {
let offsets = concat.row_offsets();
let first = offsets
.partition_point(|&offset| offset <= rows.start)
.saturating_sub(1);
let end = offsets.partition_point(|&offset| offset < rows.end);
let rows = rows.clone();
(first..end).filter_map(move |index| {
let start = offsets[index];
let chunk_end = offsets
.get(index + 1)
.copied()
.unwrap_or_else(|| concat.row_count());
let local = rows.start.max(start)..rows.end.min(chunk_end);
(local.start < local.end).then_some((index, start, local))
})
}
#[cfg(test)]
impl Shares {
pub(crate) fn pending(&self) -> usize {
self.ports
.iter()
.flat_map(|ports| ports.values())
.map(SmallVec::len)
.sum()
}
}